jpatemplate

jpatemplate
jpatemplate

8.4 集成JPA

JPA全称为Java持久性API(Java Persistence API),JPA是Java EE 5标准之一,是一个ORM规范,由厂商来实现该规范,目前有Hibernate、OpenJPA、TopLink、EclipseJPA等实现。

8.4.1 如何集成

Spring目前提供集成Hibernate、OpenJPA、TopLink、EclipseJPA四个JPA标准实现。

Spring通过使用如下Bean进行集成JPA(EntityManagerFactory):

?LocalEntityManagerFactoryBean:适用于那些仅使用JPA进行数据访问的项目,该FactoryBean将根据JPA PersistenceProvider自动检测配置文件进

行工作,一般从“META-INF/persistence.xml”读取配置信息,这种方式最简单,但不能设置Spring中定义的DataSource,且不支持Spring管理的全局事务,

而且JPA 实现商可能在JVM启动时依赖于VM agent从而允许它们进行持久化

类字节码转换(不同的实现厂商要求不同,需要时阅读其文档),不建议使用这种

方式;

persistenceUnitName:指定持久化单元的名称;

使用方式:

java代码:

Java代码

1.

2.

3.

?从JNDI中获取:用于从Java EE服务器获取指定的EntityManagerFactory,这种方式在进行Spring事务管理时一般要使用JTA事务管理;

使用方式:

java代码:

Java代码

1.

2. xmlns:xsi="http://www.w

https://www.360docs.net/doc/4611852622.html,/2001/XMLSchema-instance"

3. xmlns:jee="https://www.360docs.net/doc/4611852622.html,/schema/jee"

4. xsi:schemaLocation="

5. https://www.360docs.net/doc/4611852622.html,/schema/beans

6. https://www.360docs.net/doc/4611852622.html,/schema/beans/spring-beans-3.0

.xsd

7. https://www.360docs.net/doc/4611852622.html,/schema/jee

8. https://www.360docs.net/doc/4611852622.html,/schema/jee/spring-jee-3.0.xsd"

>

9.

10.

此处需要使用“jee”命名标签,且使用标签进行JNDI查找,

“jndi-name”属性用于指定JNDI名字。

LocalContainerEntityManagerFactoryBean:适用于所有环境的FactoryBean,能全面控制EntityManagerFactory配置,如指定Spring定义的

DataSource等等。

persistenceUnitManager:用于获取JPA持久化单元,默认实现DefaultPersistenceUnitManager用于解决多配置文件情况

dataSource:用于指定Spring定义的数据源;

persistenceXmlLocation:用于指定JPA配置文件,对于对配置文件情况请选择设置persistenceUnitManager属性来解决;

persistenceUnitName:用于指定持久化单元名字;

persistenceProvider:用于指定持久化实现厂商类;如Hibernate为

org.hibernate.ejb.HibernatePersistence类;

jpaVendorAdapter:用于设置实现厂商JPA实现的特定属性,如设置Hibernate的是否自动生成DDL的属性generateDdl;这些属性是厂商特定的,因此最好在这里设置;目前Spring提供HibernateJpaVendorAdapter、OpenJpaVendorAdapter、EclipseLinkJpaVendorAdapter、TopLinkJpaVendorAdapter、OpenJpaVendorAdapter 四个实现。其中最重要的属性是“database”,用来指定使用的数据库类型,从而能根据数据库类型来决定比如如何将数据库特定异常转换为Spring的一致性异常,目前支持如下数据库(DB2、DERBY、H2、HSQL、INFORMIX、MYSQL、ORACLE、POSTGRESQL、SQL_SERVER、SYBASE)。

jpaDialect:用于指定一些高级特性,如事务管理,获取具有事务功能的连接对象等,目前Spring提供HibernateJpaDialect、OpenJpaDialect 、EclipseLinkJpaDialect、TopLinkJpaDialect、和DefaultJpaDialect实现,注意DefaultJpaDialect不提供任何功能,因此在使用特定实现厂商JPA实现时需要指定JpaDialect实现,如使用Hibernate就使用HibernateJpaDialect。当指定jpaVendorAdapter属性时可以不指定jpaDialect,会自动设置相应的JpaDialect 实现;

jpaProperties和jpaPropertyMap:指定JPA属性;如Hibernate中指定是否显示SQL的“hibernate.show_sql”属性,对于jpaProperties设置的属性自动会放进jpaPropertyMap中;

loadTimeWeaver:用于指定LoadTimeWeaver实现,从而允许JPA 加载时修改相应的类文件。具体使用得参考相应的JPA规范实现厂商文档,如Hibernate就不需要指定loadTimeWeaver。

接下来学习一下Spring如何集成JPA吧:

1、准备jar包,从下载的hibernate-distribution-3.6.0.Final包中获取如下Hibernate需要的jar包从而支持JPA:

lib\jpa\hibernate-jpa-2.0-api-1.0.0.Final.jar //用于支持JPA

2、对象模型定义,此处使用UserModel2:

java代码:

Java代码

1.package cn.javass.spring.chapter8;

2.//省略import

3.@Entity

4.@Table(name = "test")

5.public class UserModel2 {

6. @Id @GeneratedValue(strategy = GenerationType.AUTO)

7. private int id;

8. @Column(name = "name")

9. private String myName;

10. //省略getter和setter

11.}

注意此处使用的所有注解都是位于javax.persistence包下,如使用

@org.hibernate.annotations.Entity 而非@javax.persistence. Entity将导致JPA不能正常工作。

1、 JPA配置定义(chapter8/persistence.xml),定义对象和数据库之间的映射:

java代码:

Java代码

1.

2.

3. xmlns="https://www.360docs.net/doc/4611852622.html,/xml/ns/persistence"

4. xmlns:xsi="https://www.360docs.net/doc/4611852622.html,/2001/XMLSchema-instance"

5. xsi:schemaLocation="https://www.360docs.net/doc/4611852622.html,/xml/ns/persistence

https://www.360docs.net/doc/4611852622.html,/xml/ns/persistence/persistence_1_0.xs

d">

6.

7.

8.

在JPA配置文件中,我们指定要持久化单元名字,和事务类型,其他都将在Spring 中配置。

2、数据源定义,此处使用第7章的配置文件,即

“chapter7/applicationContext-resources.xml”文件。

3、 EntityManagerFactory配置定义

(chapter8/applicationContext-jpa.xml):

java代码:

Java代码

1.

2.

3.

4.

5.

6.

7.

8.

9.

10. true

11.

12.

13.

14.

java代码:

Java代码

1.

2.

3.

4.

5.

?LocalContainerEntityManagerFactoryBean:指定使用本地容器管理EntityManagerFactory,从而进行细粒度控制;

?dataSource属性指定使用Spring定义的数据源;

?persistenceXmlLocation指定JPA配置文件为chapter8/persistence.xml,且该配置文件非常简单,具体配置完全在Spring中进行;

?persistenceUnitName指定持久化单元名字,即JPA配置文件中指定的;

?persistenceProvider:指定JPA持久化提供商,此处使用Hibernate实现HibernatePersistence类;

?jpaVendorAdapter:指定实现厂商专用特性,即generateDdl= false表示不自动生成DDL,database= HSQL表示使用hsqldb数据库;

?jpaDialect:如果指定jpaVendorAdapter此属性可选,此处为

HibernateJpaDialect;

?jpaProperties:此处指定“hibernate.show_sql =true”表示在日志系统debug级别下将打印所有生成的SQL。

4、获取EntityManagerFactory:

java代码:

Java代码

1.package cn.javass.spring.chapter8;

2.//省略import

3.public class JPATest {

4. private static EntityManagerFactory entityManagerFactory;

5. @BeforeClass

6. public static void setUpClass() {

7. String[] configLocations = new String[] {

8. "classpath:chapter7/applicationContext-resources.xml",

9. "classpath:chapter8/applicationContext-jpa.xml"};

10. ApplicationContext ctx = new ClassPathXmlApplicationContext(c

onfigLocations);

11. entityManagerFactory = ctx.getBean(EntityManagerFactory.class

);

12. }

13.}

此处我们使用了chapter7/applicationContext- resources.xml定义的“dataSource”数据源,通过ctx.getBean(EntityManagerFactory.class)获取EntityManagerFactory。

5、通过EntityManagerFactory获取EntityManager进行创建和删除表:

java代码:

Java代码

1.@Before

2.public void setUp() throws SQLException {

3. //id自增主键从0开始

4. String createTableSql = "create memory table test" + "(id int GENER

ATED BY DEFAULT AS IDENTITY PRIMARY KEY, " + "name varchar(10

0))";

5. executeSql(createTableSql);

6.}

7.@After

8.public void tearDown() throws SQLException {

9. String dropTableSql = "drop table test";

10. executeSql(dropTableSql);

11.}

12.

13.p rivate void executeSql(String sql) throws SQLException {

14. EntityManager em = entityManagerFactory.createEntityManager();

15. beginTransaction(em);

16. em.createNativeQuery(sql).executeUpdate();

17. commitTransaction(em);

18. closeEntityManager(em);

19.}

20.p rivate void closeEntityManager(EntityManager em) {

21. em.close();

22.}

23.p rivate void rollbackTransacrion(EntityManager em) throws SQLExcep

tion {

24. if(em != null) {

25. em.getTransaction().rollback();

26. }

27.}

28.p rivate void commitTransaction(EntityManager em) throws SQLExcept

ion {

29. em.getTransaction().commit();

30.}

31.p rivate void beginTransaction(EntityManager em) throws SQLExceptio

n {

32. em.getTransaction().begin();

33.}

使用EntityManagerFactory创建EntityManager,然后通过EntityManager对象的createNativeQuery创建本地SQL执行创建和删除表。

6、使用EntityManagerFactory获取EntityManager对象进行持久化数据:

java代码:

Java代码

1.@Test

2.public void testFirst() throws SQLException {

3. UserModel2 model = new UserModel2();

4. model.setMyName("test");

5. EntityManager em = null;

6. try {

7. em = entityManagerFactory.createEntityManager();

8. beginTransaction(em);

9. em.persist(model);

10. commitTransaction(em);

11. } catch (SQLException e) {

12. rollbackTransacrion(em);

13. throw e;

14. } finally {

15. closeEntityManager(em);

16. }

17.}

使用EntityManagerFactory获取EntityManager进行操作,看到这还能忍受冗长的代码和事务管理吗?Spring同样提供JpaTemplate模板类来简化这些操作。

大家有没有注意到此处的模型对象能自动映射到数据库,这是因为Hibernate JPA实现默认自动扫描类路径中的@Entity注解类及*.hbm.xml映射文件,可以通过更改Hibernate JPA属性“hibernate.ejb.resource_scanner”,并指定

org.hibernate.ejb.packaging.Scanner接口实现来定制新的扫描策略。

8.4.2 使用JpaTemplate

JpaTemplate模板类用于简化事务管理及常见操作,类似于JdbcTemplate模板类,对于复杂操作通过提供JpaCallback回调接口来允许更复杂的操作。

接下来示例一下JpaTemplate的使用:

1、修改Spring配置文件(chapter8/applicationContext-jpa.xml),添加JPA 事务管理器:

java代码:

Java代码

1.

2.

3.

4.

txManager:指定事务管理器,JPA使用JpaTransactionManager事务管理器实现,通过entityManagerFactory指定EntityManagerFactory;用于支持

Java SE环境的JPA扩展的持久化上下文(EXTENDED Persistence

Context)。

2、修改JPATest类,添加类变量ctx,用于后边使用其获取事务管理器使用:

java代码:

Java代码

1.package cn.javass.spring.chapter8;

2.public class JPATest {

3. private static EntityManagerFactory entityManagerFactory;

4. private static ApplicationContext ctx;

5. @BeforeClass

6. public static void beforeClass() {

7. String[] configLocations = new String[] {

8. "classpath:chapter7/applicationContext-resources.xml",

9. "classpath:chapter8/applicationContext-jpa.xml"};

10. ctx = new ClassPathXmlApplicationContext(configLocations);

11. entityManagerFactory = ctx.getBean(EntityManagerFactory.class

);

12.}

13.}

14.

3)JpaTemplate模板类使用:

java代码:

Java代码

1.@Test

2.public void testJpaTemplate() {

3.final JpaTemplate jpaTemplate = new JpaTemplate(entityManagerFacto

ry);

4. final UserModel2 model = new UserModel2();

5. model.setMyName("test1");

6. PlatformTransactionManager txManager = ctx.getBean(PlatformTrans

actionManager.class);

7. new TransactionTemplate(txManager).execute(

8. new TransactionCallback() {

9. @Override

10. public Void doInTransaction(TransactionStatus status) {

11. jpaTemplate.persist(model);

12. return null;

13. }

14. });

15. String COUNT_ALL = "select count(*) from UserModel";

16. Number count = (Number) jpaTemplate.find(COUNT_ALL).get(0);

17. Assert.assertEquals(1, count.intValue());

18.}

19.

20.

?jpaTemplate:可通过new JpaTemplate(entityManagerFactory)方式创建;

?txManager:通过ctx.getBean(PlatformTransactionManager.class)获取事务管理器;

?TransactionTemplate:通过new TransactionTemplate(txManager)创建事务模板对象,并通过execute方法执行TransactionCallback 回调中的

doInTransaction方法中定义需要执行的操作,从而将由模板类通过txManager

事务管理器来进行事务管理,此处是调用jpaTemplate对象的persist方法进行

持久化;

?jpaTemplate.persist():根据JPA规范,在JPA扩展的持久化上下文,该操作必须运行在事务环境,还有persist()、merge()、remove()操作也必须运行

在事务环境;

?jpaTemplate.find():根据JPA规范,该操作无需运行在事务环境,还有find()、getReference()、refresh()、detach()和查询操作都无需运行在事务环境。

此实例与Hibernate和Ibatis有所区别,通过JpaTemplate模板类进行如持久化等操作时必须有运行在事务环境中,否则可能抛出如下异常或警告:

?“javax.persistence.TransactionRequiredE xception:Executing an update/delete query”:表示没有事务支持,不能执行更新或删除操作;

?警告“delaying identity-insert due to no transaction in progress”:需要在日志系统启动debug模式才能看到,表示在无事务环境中无法进行持久化,而选择了延迟标识插入。

以上异常和警告是没有事务造成的,也是最让人困惑的问题,需要大家注意。

8.4.3 集成JPA及最佳实践

类似于JdbcDaoSupport类,Spring对JPA也提供了JpaDaoSupport类来支持一致的数据库访问。JpaDaoSupport也是DaoSupport实现:

接下来示例一下Spring集成JPA的最佳实践:

1、定义Dao接口,此处使用cn.javass.spring.chapter7.dao. IUserDao:

2、定义Dao接口实现,此处是JPA实现:

java代码:

Java代码

1.package cn.javass.spring.chapter8.dao.jpa;

2.//省略import

3.@Transactional(propagation = Propagation.REQUIRED)

4.public class UserJpaDaoImpl extends JpaDaoSupport implements IUser

Dao {

5. private static final String COUNT_ALL_JPAQL = "select count(*) fro

m UserModel";

6. @Override

7. public void save(UserModel model) {

8. getJpaTemplate().persist(model);

9. }

10. @Override

11. public int countAll() {

12. Number count =

13. (Number) getJpaTemplate().find(COUNT_ALL_JPAQL).get(0);

14. return count.intValue();

15. }

16.}

此处注意首先JPA实现放在dao.jpa包里,其次实现类命名如UserJpaDaoImpl,即

×××JpaDaoImpl,当然如果自己有更好的命名规范可以遵循自己的,此处只是提个建议。

另外在类上添加了@Transactional注解表示该类的所有方法将在调用时需要事务支持,propagation传播属性为Propagation.REQUIRED表示事务是必需的,如果执行该类的方法没有开启事务,将开启一个新的事务。

3、进行资源配置,使用

resources/chapter7/applicationContext-resources.xml:

4、dao定义配置,在chapter8/applicationContext-jpa.xml中添加如下配置:4.1、首先添加tx命名空间用于支持事务:

java代码:

Java代码

1.

2.

3. xmlns:xsi="https://www.360docs.net/doc/4611852622.html,/2001/XMLSchema-instance"

4. xmlns:tx="https://www.360docs.net/doc/4611852622.html,/schema/tx"

5. xsi:schemaLocation="

6. https://www.360docs.net/doc/4611852622.html,/schema/beans

7. https://www.360docs.net/doc/4611852622.html,/schema/beans/spring-beans-3.0

.xsd

8. https://www.360docs.net/doc/4611852622.html,/schema/tx

9. https://www.360docs.net/doc/4611852622.html,/schema/tx/spring-tx-3.0.xsd"> 4.2、为@Transactional注解事务开启事务支持:

java代码:

Java代码

1.

只为类添加@Transactional注解是不能支持事务的,需要通过

标签来开启事务支持,其中txManager属性指定事务管理器。

4.3、配置DAO Bean:

java代码:

Java代码

1.

2.

3.

4.

5. class="https://www.360docs.net/doc/4611852622.html,erJpaDaoImpl"

6. parent="abstractDao"/>

首先定义抽象的abstractDao,其有一个entityManagerFactory属性,从而可以让继承的子类自动继承entityManagerFactory属性注入;然后定义userDao,且继承abstractDao,从而继承entityManagerFactory注入;我们在此给配置文件命名为applicationContext-jpa.xml表示JPA实现。

5、最后测试一下吧(cn.javass.spring.chapter8. JPATest):

java代码:

Java代码

1.@Test

2.public void testBestPractice() {

3. String[] configLocations = new String[] {

4. "classpath:chapter7/applicationContext-resources.xml",

5. "classpath:chapter8/applicationContext-jpa.xml"};

6. ApplicationContext ctx = new ClassPathXmlApplicationContext(confi

gLocations);

7. IUserDao userDao = ctx.getBean(IUserDao.class);

8. UserModel model = new UserModel();

9. model.setMyName("test");

10. userDao.save(model);

11. Assert.assertEquals(1, userDao.countAll());

12.}

和Spring JDBC框架的最佳实践完全一样,除了使用applicationContext-jpa.xml代替了applicationContext-jdbc.xml,其他完全一样。也就是说,DAO层的实现替换可以透明化。

还有与集成其他ORM框架不同的是JPA在进行持久化或更新数据库操作时需要事务支持。

8.4.4 Spring+JPA的CRUD

Spring+JPA CRUD(增删改查)也相当简单,让我们直接看具体示例吧:

java代码:

Java代码

1.@Test

2.public void testCRUD() {

3. PlatformTransactionManager txManager = ctx.getBean(PlatformTran

sactionManager.class);

4. final JpaTemplate jpaTemplate = new JpaTemplate(entityManagerFa

ctory);

5. TransactionTemplate tansactionTemplate = new TransactionTemplat

e(txManager);

6. tansactionTemplate.execute(new TransactionCallback() {

7. @Override

8. public Void doInTransaction(TransactionStatus status) {

9. UserModel model = new UserModel();

10. model.setMyName("test");

11. //新增

12. jpaTemplate.persist(model);

13. //修改

14. model.setMyName("test2");

15. jpaTemplate.flush();//可选

16. //查询

17. String sql = "from UserModel where myName=?";

18. List result = jpaTemplate.find(sql, "test2");

19. Assert.assertEquals(1, result.size());

20. //删除

21. jpaTemplate.remove(model);

22. return null;

23. }

24. });

25.}

?对于增删改必须运行在事务环境,因此我们使用TransactionTemplate事务模板类来支持事务。

?持久化:使用JpaTemplate 类的persist方法持久化模型对象;

?更新:对于持久化状态的模型对象直接修改属性,调用flush方法即可更新到数据库,在一些场合时flush方法调用可选,如执行一个查询操作等,具体请参考相关

文档;

?查询:可以使用find方法执行JPA QL查询;

?删除:使用remove方法删除一个持久化状态的模型对象。

Spring集成JPA进行增删改查也相当简单,但本文介绍的稍微复杂一点,因为牵扯到编程式事务,如果采用声明式事务将和集成Hibernate方式一样简洁。

原创内容,转载请注明出处【https://www.360docs.net/doc/4611852622.html,/forum/blogPost/list/0/2500.html】

24

6

分享到:

【第九章】 Spring的事务之 9.1 数据库 ... | 跟我学spring3 电子书下载

?2012-03-04 07:29

?浏览 7993

?评论(6)

?分类:企业架构

?相关推荐

评论

6 楼Ping_QC 2012-06-21

你好,我写的spring+jpa,可以在junit上跑,

但是放到jetty时,增删改就报错了,是没有运行在事务环境中?

配置文件中加了

transaction-manager="transactionManager" proxy-target-class="true" />

错误:

javax.persistence.TransactionRequiredException: Executing an

update/delete query

5 楼jinnianshilongnian 2012-03-23

nick.s.ni 写道

Java代码

1.不会出现您说的每个Dao一个EM,而是每个事务一个

是嗎,Spring只提供了JpaTemplate,怎麼用是開發人員的事,如果不瞭解JPA,很容易出現問題。等我寫個案例給你看看。

你可以看下源码是这样的而且JpaTemplate是在execute时才决定创建/使用线程绑定的EM的

4 楼nick.s.ni 2012-03-23

Java代码

1.不会出现您说的每个Dao一个EM,而是每个事务一个

是嗎,Spring只提供了JpaTemplate,怎麼用是開發人員的事,如果不瞭解JPA,很容易出現問題。等我寫個案例給你看看。

3 楼jinnianshilongnian 2012-03-23

nick.s.ni 写道

JPA的實現我用的是EclipseLink(TopLink)實現,不會跟Hibernate混淆。你這篇應該直接提供

Java代码

2.@PersistenceContext

3.private EntityManager em;

4.public Collection loadProductsByCategory(String category) {

5.Query query = em.createQuery("from Product as p where p.categor

y = :category");

6.query.setParameter("category", category);

7.return query.getResultList();

8.}

9.}

其他的刪掉或是一句話帶過就好。

这个倒是 spring3.1也推荐用这种原生的方式,

这个我在注解配置那章(第12章)写的。

但文章肯定要提供所有场景。

使用JpaTemplate 也是没有问题的,不会出现您说的每个Dao一个EM,而是每个事务一个

2 楼jinnianshilongnian 2012-03-23

nick.s.ni 写道

spring 3已經不推薦使用JpaTemplate。

那看下spring 2.5.5中使用JpaTemplate的方式

第一種,使用JpaTemplate

Java代码

1.

2.

3.

4.

5.

Java代码

2.private JpaTemplate jpaTemplate;

3.public void setEntityManagerFactory(EntityManagerFactory emf) {

4.this.jpaTemplate = new JpaTemplate(emf);

5.}

6.public Collection loadProductsByCategory(final String category)

throws DataAccessException {

7.return (Collection) this.jpaTemplate.execute(new JpaCallback()

{

8.public Object doInJpa(EntityManager em) throws PersistenceExcep

tion {

9.Query query = em.createQuery("from Product as p where p.categor

y = :category");

10.query.setParameter("category", category);

11.List result = query.getResultList();

12.// do some further processing with the result list

13.return result;

14.}

15.});

16.}

17.}

第二種

繼承JpaDaoSupport

Java代码

1.public class ProductDaoImpl extends JpaDaoSupport implements Pr

oductDao {

2.public Collection loadProductsByCategory(String category) throw

s DataAccessException {

3.Map params = new HashMap();

4.params.put("category", category);

5.return getJpaTemplate().findByNamedParams("from Product as p wh

ere p.category = :category", params);

6.}

7.}

JpaDaoSupport中需要注入EMF,而EM是內部創建的

Java代码

1.protected EntityManager createEntityManager() throws IllegalSta

teException {

2. EntityManagerFactory emf = getEntityManagerFactory();

3. Assert.state(emf != null, "No EntityManagerFactory spec

ified");

4. Map properties = getJpaPropertyMap();

5. return (!CollectionUtils.isEmpty(properties) ? emf.crea

teEntityManager(properties) : emf.createEntityManager());

6. }

這不是代碼問題是使用問題,每個DAO都有一個EM,EM中有一定的數據緩存,根據EM的實現,對數據庫中的新增數據會更新,對修改后的數據在查詢時不會更新。

例如一個DAO進行連表查詢,另一個DAO中進行子表的CRUD,修改的信息在查詢中不會更新的。

nick.s.ni 写道

spring 3已經不推薦使用JpaTemplate。

那看下spring 2.5.5中使用JpaTemplate的方式

第一種,使用JpaTemplate

Java代码

1.

2.

3.

4.

5.

Java代码

1.public class JpaProductDao implements ProductDao {

2.private JpaTemplate jpaTemplate;

3.public void setEntityManagerFactory(EntityManagerFactory emf) {

4.this.jpaTemplate = new JpaTemplate(emf);

5.}

6.public Collection loadProductsByCategory(final String category)

throws DataAccessException {

相关主题
相关文档
最新文档