Spring详解-----------对ORM的支持

来源:互联网 发布:歼31是不是下马了知乎 编辑:程序博客网 时间:2024/06/05 10:54

8.1  概述

8.1.1  ORM框架

       ORM全称对象关系映射(Object/Relation Mapping),指将Java对象状态自动映射到关系数据库中的数据上,从而提供透明化的持久化支持,即把一种形式转化为另一种形式。

       对象与关系数据库之间是不匹配,我们把这种不匹配称为阻抗失配,主要表现在:

  • 关系数据库首先不支持面向对象技术如继承、多态,如何使关系数据库支持它们;
  • 关系数据库是由表来存放数据,而面向对象使用对象来存放状态;其中表的列称为属性,而对象的属性就是属性,因此需要通过解决这种不匹配;
  • 如何将对象透明的持久化到关系数据库表中;
  • 如果一个对象存在横跨多个表的数据,应该如何为对象建模和映射。

其中这些阻抗失配只是其中的一小部分,比如还有如何将SQL集合函数结果集映射到对象,如何在对象中处理主键等。

ORM框架就是用来解决这种阻抗失配,提供关系数据库的对象化支持。

ORM框架不是万能的,同样符合80/20法则,应解决的最核心问题是如何在关系数据库表中的行和对象进行映射,并自动持久化对象到关系数据库。

ORM解决方案适用于解决透明持久化、小结果集查询等;对于复杂查询,大结果集数据处理还是没有任何帮助的。

目前已经有许多ORM框架产生,如Hibernate、JDO、JPA、iBATIS等等,这些ORM框架各有特色,Spring对这些ORM框架提供了很好的支持,接下来首先让我们看一下Spring如何支持这些ORM框架。

8.1.2  Spring对ORM的支持

       Spring对ORM的支持主要表现在以下方面:

  • 一致的异常体系结构,对第三方ORM框架抛出的专有异常进行包装,从而在使我们在Spring中只看到DataAccessException异常体系;
  • 一致的DAO抽象支持:提供类似与JdbcSupport的DAO支持类HibernateDaoSupport,使用HibernateTemplate模板类来简化常用操作,HibernateTemplate提供回调接口来支持复杂操作;
  • Spring事务管理:Spring对所有数据访问提供一致的事务管理,通过配置方式,简化事务管理。

Spring还在测试、数据源管理方面提供支持,从而允许方便测试,简化数据源使用。

接下来让我们学习一下Spring如何集成ORM框架—Hibernate。


8.2  集成Hibernate3

       Hibernate是全自动的ORM框架,能自动为对象生成相应SQL并透明的持久化对象到数据库。

Spring2.5+版本支持Hibernate 3.1+版本,不支持低版本,Spring3.0.5版本提供对Hibernate 3.6.0 Final版本支持。

8.2.1  如何集成

       Spring通过使用如下Bean进行集成Hibernate:

  • LocalSessionFactoryBean :用于支持XML映射定义读取:

         configLocation和configLocations:用于定义Hibernate配置文件位置,一般使用如classpath:hibernate.cfg.xml形式指定;

         mappingLocations :用于指定Hibenate映射文件位置,如chapter8/hbm/user.hbm.xml;

         hibernateProperties:用于定义Hibernate属性,即Hibernate配置文件中的属性;

         dataSource:定义数据源;

hibernateProperties、dataSource用于消除Hibernate配置文件,因此如果使用configLocations指定配置文件,就不要设置这两个属性了,否则会产生重复配置。推荐使用dataSource来指定数据源,而使用hibernateProperties指定Hibernate属性。

  • AnnotationSessionFactoryBean:用于支持注解风格映射定义读取,该类继承LocalSessionFactoryBean并额外提供自动查找注解风格配置模型的能力:

         annotatedClasses:设置注解了模型类,通过注解指定映射元数据。

         packagesToScan:通过扫描指定的包获取注解模型类,而不是手工指定,如“cn.javass.**.model”将扫描cn.javass包及子包下的model包下的所有注解模型类。

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

 

1、准备jar包:

首先准备Spring对ORM框架支持的jar包:

org.springframework.orm-3.0.5.RELEASE.jar  //提供对ORM框架集成

 

下载hibernate-distribution-3.6.0.Final包,获取如下Hibernate需要的jar包:

hibernate3.jar                       //核心包

lib\required\antlr-2.7.6.jar              //HQL解析时使用的包

lib\required\javassist-3.9.0.GA.jar       //字节码类库,类似于cglib

lib\required\commons-collections-3.1.jar  //对集合类型支持包,前边测试时已经提供过了,无需再拷贝该包了

lib\required\dom4j-1.6.1.jar            //xml解析包,用于解析配置使用

lib\required\jta-1.1.jar                 //JTA事务支持包

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

 

 

下载slf4j-1.6.1.zip(http://www.slf4j.org/download.html),slf4j是日志系统门面(Simple Logging Facade for Java),用于对各种日志框架提供给一致的日志访问接口,从而能随时替换日志框架(如log4j、java.util.logging):

 

slf4j-api-1.6.1.jar              //核心API

slf4j-log4j12-1.6.1.jar          //log4j实现

 

将这些jar包添加到类路径中。

 

2、对象模型定义,此处使用第七章中的UserModel:

 

java代码:
Java代码  收藏代码
  1. package cn.javass.spring.chapter7;  
  2. public class UserModel {  
  3.     private int id;  
  4.     private String myName;  
  5.     //省略getter和setter  
  6. }  

 

3、Hibernate映射定义(chapter8/hbm/user.hbm.xml),定义对象和数据库之间的映射:

 

java代码:
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC  
  3.         "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.         "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping>  
  6.     <class name="cn.javass.spring.chapter7.UserModel" table="test">  
  7.         <id name="id" column="id"><generator class="native"/></id>  
  8.         <property name="myName" column="name"/>  
  9.     </class>  
  10. </hibernate-mapping>  

 

4、   数据源定义,此处使用第7章的配置文件,即“chapter7/applicationContext-resources.xml”文件。

 

5、   SessionFactory配置定义(chapter8/applicationContext-hibernate.xml):

 

java代码:
Java代码  收藏代码
  1. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  2.     <property name="dataSource" ref="dataSource"/> <!-- 指定数据源 -->  
  3.       <property name="mappingResources">     <!-- 指定映射定义 -->  
  4.         <list>  
  5.           <value>chapter8/hbm/user.hbm.xml</value>  
  6.         </list>  
  7.       </property>  
  8.       <property name="hibernateProperties">   <!--指定Hibernate属性 -->  
  9.         <props>  
  10.           <prop key="hibernate.dialect">  
  11.               org.hibernate.dialect.HSQLDialect  
  12.           </prop>  
  13.         </props>  
  14.       </property>  
  15. </bean>  

 

6、   获取SessionFactory:

 

java代码:
Java代码  收藏代码
  1. package cn.javass.spring.chapter8;  
  2. //省略import  
  3. public class HibernateTest {  
  4.     private static SessionFactory sessionFactory;  
  5.     @BeforeClass  
  6.     public static void beforeClass() {  
  7.         String[] configLocations = new String[] {  
  8.                 "classpath:chapter7/applicationContext-resources.xml",  
  9.                 "classpath:chapter8/applicationContext-hibernate.xml"};  
  10.         ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);  
  11.         sessionFactory = ctx.getBean("sessionFactory", SessionFactory.class);  
  12. }  
  13. }  

 

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

 

7、通过SessionFactory获取Session对象进行创建和删除表:

 

java代码:
Java代码  收藏代码
  1. @Before  
  2. public void setUp() {  
  3.   //id自增主键从0开始  
  4.   final String createTableSql = "create memory table test" + "(id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " + "name varchar(100))";  
  5.   sessionFactory.openSession().  
  6.   createSQLQuery(createTableSql).executeUpdate();  
  7. }  
  8. @After  
  9. public void tearDown() {  
  10.     final String dropTableSql = "drop table test";  
  11.     sessionFactory.openSession().  
  12.     createSQLQuery(dropTableSql).executeUpdate();  
  13. }  

 

使用SessionFactory创建Session,然后通过Session对象的createSQLQuery创建本地SQL执行创建和删除表。

 

8、使用SessionFactory获取Session对象进行持久化数据:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testFirst() {  
  3.     Session session = sessionFactory.openSession();  
  4.     Transaction transaction = null;  
  5.     try {  
  6.         transaction = beginTransaction(session);  
  7.         UserModel model = new UserModel();  
  8.         model.setMyName("myName");  
  9.         session.save(model);  
  10.     } catch (RuntimeException e) {  
  11.         rollbackTransaction(transaction);  
  12.         throw e;  
  13.     } finally {  
  14.         commitTransaction(session);  
  15.     }  
  16. }  

 

 

java代码:
Java代码  收藏代码
  1. private Transaction beginTransaction(Session session) {  
  2.     Transaction transaction = session.beginTransaction();  
  3.     transaction.begin();  
  4.     return transaction;  
  5. }  
  6. private void rollbackTransaction(Transaction transaction) {  
  7.    if(transaction != null) {  
  8.         transaction.rollback();  
  9.     }  
  10. }  
  11. private void commitTransaction(Session session) {  
  12.     session.close();  
  13. }  
  14.    

 

 

使用SessionFactory获取Session进行操作,必须自己控制事务,而且还要保证各个步骤不会出错,有没有更好的解决方案把我们从编程事务中解脱出来?Spring提供了HibernateTemplate模板类用来简化事务处理和常见操作。

 

8.2.2 使用HibernateTemplate

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

 

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

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testHibernateTemplate() {  
  3. HibernateTemplate hibernateTemplate =  
  4. new HibernateTemplate(sessionFactory);  
  5.     final UserModel model = new UserModel();  
  6.     model.setMyName("myName");  
  7.     hibernateTemplate.save(model);  
  8.     //通过回调允许更复杂操作  
  9.     hibernateTemplate.execute(new HibernateCallback<Void>() {  
  10.         @Override  
  11.         public Void doInHibernate(Session session)  
  12.             throws HibernateException, SQLException {  
  13.             session.save(model);  
  14.             return null;  
  15.         }});  
  16. }  
  17.    

 

通过new HibernateTemplate(sessionFactory) 创建HibernateTemplate模板类对象,通过调用模板类的save方法持久化对象,并且自动享受到Spring管理事务的好处。

而且HibernateTemplate 提供使用HibernateCallback回调接口的方法execute用来支持复杂操作,当然也自动享受到Spring管理事务的好处。

 

8.2.3  集成Hibernate及最佳实践

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

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

 

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

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

 

java代码:
Java代码  收藏代码
  1.       
  2. package cn.javass.spring.chapter8.dao.hibernate;  
  3. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;  
  4. import cn.javass.spring.chapter7.UserModel;  
  5. import cn.javass.spring.chapter7.dao.IUserDao;  
  6. public class UserHibernateDaoImpl extends HibernateDaoSupport implements IUserDao {  
  7.     private static final String COUNT_ALL_HQL = "select count(*) from UserModel";  
  8.     @Override  
  9.     public void save(UserModel model) {  
  10.         getHibernateTemplate().save(model);  
  11.     }  
  12.     @Override  
  13.     public int countAll() {  
  14.         Number count = (Number) getHibernateTemplate().find(COUNT_ALL_HQL).get(0);  
  15.         return count.intValue();  
  16.     }  
  17. }  

 

此处注意首先Hibernate实现放在dao.hibernate包里,其次实现类命名如UserHibernateDaoImpl,即×××HibernateDaoImpl,当然如果自己有更好的命名规范可以遵循自己的,此处只是提个建议。

 

3、进行资源配置,使用resources/chapter7/applicationContext-resources.xml:

 

4、dao定义配置,在chapter8/applicationContext-hibernate.xml中添加如下配置:

 

 

java代码:
Java代码  收藏代码
  1. <bean id="abstractDao" abstract="true">  
  2.     <property name="sessionFactory" ref="sessionFactory"/>  
  3. </bean>     
  4. <bean id="userDao"  class="cn.javass.spring.chapter8.dao.hibernate.UserHibernateDaoImpl" parent="abstractDao"/>     

 

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

 

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

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testBestPractice() {  
  3.     String[] configLocations = new String[] {  
  4.             "classpath:chapter7/applicationContext-resources.xml",  
  5.             "classpath:chapter8/applicationContext-hibernate.xml"};  
  6.     ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);  
  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-hibernate.xml代替了applicationContext-jdbc.xml,其他完全一样。也就是说,DAO层的实现替换可以透明化。

 

8.2.4  Spring+Hibernate的CRUD

       Spring+Hibernate CRUD(增删改查)我们使用注解类来示例,让我们看具体示例吧:

1、首先定义带注解的模型对象UserModel2:

  • 使用JPA注解@Table指定表名映射;
  • 使用注解@Id指定主键映射;
  • 使用注解@ Column指定数据库列映射;

 

java代码:
Java代码  收藏代码
  1. package cn.javass.spring.chapter8;  
  2. import javax.persistence.Column;  
  3. import javax.persistence.Entity;  
  4. import javax.persistence.GeneratedValue;  
  5. import javax.persistence.GenerationType;  
  6. import javax.persistence.Id;  
  7. import javax.persistence.Table;  
  8. @Entity  
  9. @Table(name = "test")  
  10. public class UserModel2 {  
  11.     @Id @GeneratedValue(strategy = GenerationType.AUTO)  
  12.     private int id;  
  13.     @Column(name = "name")  
  14.     private String myName;  
  15.     //省略getter和setter  
  16. }  

 

2、 定义配置文件(chapter8/applicationContext-hibernate2.xml):

 

2.1、        定义SessionFactory:

此处使用AnnotationSessionFactoryBean通过annotatedClasses属性指定注解模型来定义映射元数据;

 

java代码:
Java代码  收藏代码
  1. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">  
  2. <property name="dataSource" ref="dataSource"/>  <!-- 1、指定数据源 -->  
  3.   <property name="annotatedClasses">           <!-- 2、指定注解类 -->  
  4.      <list><value>cn.javass.spring.chapter8.UserModel2</value></list>  
  5. </property>  
  6.   <property name="hibernateProperties"><!-- 3、指定Hibernate属性 -->  
  7.     <props>  
  8.      <prop key="hibernate.dialect">  
  9.         org.hibernate.dialect.HSQLDialect  
  10.      </prop>  
  11.     </props>  
  12.   </property>  
  13. </bean>  

 

2.2、定义HibernateTemplate :

 

java代码:
Java代码  收藏代码
  1. <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">  
  2.     <property name="sessionFactory" ref="sessionFactory"/>  
  3. </bean>  

 

3、最后进行CURD测试吧:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testCURD() {  
  3.     String[] configLocations = new String[] {  
  4.             "classpath:chapter7/applicationContext-resources.xml",  
  5.             "classpath:chapter8/applicationContext-hibernate2.xml"};  
  6.     ApplicationContext ctx =  new ClassPathXmlApplicationContext(configLocations);  
  7.     HibernateTemplate hibernateTemplate =  ctx.getBean(HibernateTemplate.class);  
  8.     UserModel2 model = new UserModel2();  
  9.     model.setMyName("test");  
  10.     insert(hibernateTemplate, model);  
  11.     select(hibernateTemplate, model);  
  12.     update(hibernateTemplate, model);  
  13.     delete(hibernateTemplate, model);  
  14. }  
  15.   
  16. private void insert(HibernateTemplate hibernateTemplate, UserModel2 model) {  
  17.     hibernateTemplate.save(model);  
  18. }  
  19. private void select(HibernateTemplate hibernateTemplate, UserModel2 model) {  
  20.     UserModel2 model2 = hibernateTemplate.get(UserModel2.class0);  
  21.     Assert.assertEquals(model2.getMyName(), model.getMyName());  
  22.     List<UserModel2> list = hibernateTemplate.find("from UserModel2");  
  23.     Assert.assertEquals(list.get(0).getMyName(), model.getMyName());  
  24. }  
  25. private void update(HibernateTemplate hibernateTemplate, UserModel2 model) {  
  26.     model.setMyName("test2");  
  27.     hibernateTemplate.update(model);  
  28. }  
  29. private void delete(HibernateTemplate hibernateTemplate, UserModel2 model) {  
  30.     hibernateTemplate.delete(model);  
  31. }  

 

       Spring集成Hibernate进行增删改查是不是比Spring JDBC方式简单许多,而且支持注解方式配置映射元数据,从而减少映射定义配置文件数量。

 

8.3  集成iBATIS

       iBATIS是一个半自动化的ORM框架,需要通过配置方式指定映射SQL语句,而不是由框架本身生成(如Hibernate自动生成对应SQL来持久化对象),即Hibernate属于全自动ORM框架。

Spring提供对iBATIS 2.X的集成,提供一致的异常体系、一致的DAO访问支持、Spring管理事务支持。

       Spring 2.5.5+版本支持iBATIS 2.3+版本,不支持低版本。

8.3.1  如何集成

       Spring通过使用如下Bean进行集成iBATIS:

  • SqlMapClientFactoryBean:用于集成iBATIS。

         configLocation和configLocations:用于指定SQL Map XML配置文件,用于指定如数据源等配置信息;

         mappingLocations:用于指定SQL Map映射文件,即半自动概念中的SQL语句定义;

         sqlMapClientProperties:定义iBATIS 配置文件配置信息;

         dataSource:定义数据源。

 

如果在Spring配置文件中指定了DataSource,就不要在iBATIS配置文件指定了,否则Spring配置文件指定的DataSource将覆盖iBATIS配置文件中定义的DataSource。

 

       接下来示例一下如何集成iBATIS:

1、准备需要的jar包,从spring-framework-3.0.5.RELEASE-dependencies.zip中拷贝如下jar包:

 

 com.springsource.com.ibatis-2.3.4.726.jar

  

2、 对象模型定义,此处使用第七章中的UserModel;

 

3、 iBATIS映射定义(chapter8/sqlmaps/UserSQL.xml):


java代码:
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"  
  3.     "http://ibatis.apache.org/dtd/sql-map-2.dtd">  
  4. <sqlMap namespace="UserSQL">  
  5.   <statement id="createTable">  
  6.     <!--id自增主键从0开始 -->  
  7.     <![CDATA[  
  8.         create memory table test(  
  9.           id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,   
  10.           name varchar(100))  
  11.     ]]>  
  12.   </statement>  
  13.   <statement id="dropTable">  
  14.     <![CDATA[  drop table test  ]]>  
  15.   </statement>  
  16.   <insert id="insert" parameterClass="cn.javass.spring.chapter7.UserModel">  
  17.     <![CDATA[  
  18.       insert into test(name) values (#myName#)  
  19.     ]]>  
  20.     <selectKey resultClass="int" keyProperty="id" type="post">  
  21.       <!-- 获取hsqldb插入的主键 -->  
  22.       call identity();  
  23.       <!-- mysql使用select last_insert_id();获取插入的主键 -->  
  24.     </selectKey>  
  25.   </insert>  
  26. </sqlMap>  
  27.    
  28.    


4、 iBATIS配置文件(chapter8/sql-map-config.xml)定义:

 

java代码:
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"  
  3.     "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">  
  4.    
  5. <sqlMapConfig>  
  6.     <settings enhancementEnabled="true" useStatementNamespaces="true"  
  7.         maxTransactions="20" maxRequests="32" maxSessions="10"/>  
  8.     <sqlMap resource="chapter8/sqlmaps/UserSQL.xml"/>  
  9. </sqlMapConfig>  
  10.    
  11.    

 

5、 数据源定义,此处使用第7章的配置文件,即“chapter7/applicationContext-resources.xml”文件。

 

6、 SqlMapClient配置(chapter8/applicationContext-ibatis.xml)定义:

 

java代码:
Java代码  收藏代码
  1. <bean id="sqlMapClient"  
  2.     class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  
  3.     <!-- 1、指定数据源 -->  
  4.     <property name="dataSource" ref="dataSource"/>  
  5.     <!-- 2、指定配置文件 -->  
  6.     <property name="configLocation" value="chapter8/sql-map-config.xml"/>  
  7. </bean>  

 

7、 获取SqlMapClient:

 

java代码:
Java代码  收藏代码
  1. package cn.javass.spring.chapter8;  
  2. //省略import  
  3. public class IbatisTest {  
  4.     private static SqlMapClient sqlMapClient;  
  5.     @BeforeClass  
  6.     public static void setUpClass() {  
  7.       String[] configLocations = new String[] {  
  8.           "classpath:chapter7/applicationContext-resources.xml",  
  9.           "classpath:chapter8/applicationContext-ibatis.xml"};  
  10.       ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);  
  11.       sqlMapClient = ctx.getBean(SqlMapClient.class);  
  12.   }  
  13. }  

 

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

 

 

8、 通过SqlMapClient创建和删除表:

 

java代码:
Java代码  收藏代码
  1. @Before  
  2. public void setUp() throws SQLException {  
  3.     sqlMapClient.update("UserSQL.createTable");  
  4. }  
  5. @After  
  6. public void tearDown() throws SQLException {  
  7.     sqlMapClient.update("UserSQL.dropTable");  
  8. }  

 

 

9、 使用SqlMapClient进行对象持久化:


java代码:
Java代码  收藏代码
  1.       
  2. @Test  
  3. public void testFirst() throws SQLException {  
  4.     UserModel model = new UserModel();  
  5.     model.setMyName("test");  
  6.     SqlMapSession session = null;  
  7.     try {  
  8.         session = sqlMapClient.openSession();  
  9.         beginTransaction(session);  
  10.         session.insert("UserSQL.insert", model);  
  11.         commitTransaction(session);  
  12.     } catch (SQLException e) {  
  13.         rollbackTransacrion(session);  
  14.         throw e;  
  15.     } finally {  
  16.         closeSession(session);  
  17.     }  
  18. }  
  19. private void closeSession(SqlMapSession session) {  
  20.    session.close();  
  21. }  
  22. private void rollbackTransacrion(SqlMapSession session) throws SQLException {  
  23.     if(session != null) {  
  24.         session.endTransaction();  
  25.     }           
  26. }  
  27. private void commitTransaction(SqlMapSession session) throws SQLException {  
  28.     session.commitTransaction();  
  29. }  
  30. private void beginTransaction(SqlMapSession session) throws SQLException {  
  31.     session.startTransaction();  
  32. }  
  33.    


同样令人心烦的事务管理和冗长代码,Spring通用提供了SqlMapClientTemplate模板类来解决这些问题。

 

8.3.2  使用 SqlMapClientTemplate

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

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

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testSqlMapClientTemplate() {  
  3. SqlMapClientTemplate sqlMapClientTemplate =  
  4. new SqlMapClientTemplate(sqlMapClient);  
  5.     final UserModel model = new UserModel();  
  6.     model.setMyName("myName");  
  7.     sqlMapClientTemplate.insert("UserSQL.insert", model);  
  8.     //通过回调允许更复杂操作  
  9.     sqlMapClientTemplate.execute(new SqlMapClientCallback<Void>() {  
  10.         @Override  
  11.         public Void doInSqlMapClient(SqlMapExecutor session) throws SQLException {  
  12.             session.insert("UserSQL.insert", model);  
  13.             return null;  
  14.     }});  
  15. }  
  16.    

 

通过new SqlMapClientTemplate(sqlMapClient)创建HibernateTemplate模板类对象,通过调用模板类的save方法持久化对象,并且自动享受到Spring管理事务的好处。

 

而且SqlMapClientTemplate提供使用SqlMapClientCallback回调接口的方法execute用来支持复杂操作,当然也自动享受到Spring管理事务的好处。

 

8.3.3集成iBATIS及最佳实践

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

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

 

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

 

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

 

java代码:
Java代码  收藏代码
  1. package cn.javass.spring.chapter8.dao.ibatis;  
  2. //省略import  
  3. public class UserIbatisDaoImpl extends SqlMapClientDaoSupport  
  4.     implements IUserDao {  
  5.     @Override  
  6.     public void save(UserModel model) {  
  7.         getSqlMapClientTemplate().insert("UserSQL.insert", model);  
  8.     }  
  9.     @Override  
  10.     public int countAll() {  
  11.         return (Integer) getSqlMapClientTemplate().queryForObject("UserSQL.countAll");  
  12.     }  
  13. }  

 

3、修改iBATS映射文件(chapter8/sqlmaps/UserSQL.xml),添加countAll查询:

 

java代码:
Java代码  收藏代码
  1. <select id="countAll" resultClass="java.lang.Integer">  
  2.     <![CDATA[ select count(*) from test ]]>     
  3. </select>  

此处注意首先iBATIS实现放在dao.ibaitis包里,其次实现类命名如UserIbatisDaoImpl,即×××IbatisDaoImpl,当然如果自己有更好的命名规范可以遵循自己的,此处只是提个建议。

 

4、进行资源配置,使用resources/chapter7/applicationContext-resources.xml:

 

5、dao定义配置,在chapter8/applicationContext-ibatis.xml中添加如下配置:

 

java代码:
Java代码  收藏代码
  1. <bean id="abstractDao" abstract="true">  
  2.     <property name="sqlMapClient" ref="sqlMapClient"/>  
  3. </bean>     
  4. <bean id="userDao"  
  5.       class="cn.javass.spring.chapter8.dao.ibatis.UserIbatisDaoImpl"  
  6.       parent="abstractDao"/>   

 

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

 

 

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

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testBestPractice() {  
  3.     String[] configLocations = new String[] {  
  4.             "classpath:chapter7/applicationContext-resources.xml",  
  5.             "classpath:chapter8/applicationContext-ibatis.xml"};  
  6.     ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);  
  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-ibatis.xml代替了applicationContext-jdbc.xml,其他完全一样。也就是说,DAO层的实现替换可以透明化。

 

8.3.4Spring+iBATIS的CURD

Spring集成iBATIS进行CURD(增删改查),也非常简单,首先配置映射文件,然后调用SqlMapClientTemplate相应的函数进行操作即可,此处就不介绍了。

 

 

 

 

8.3.5集成MyBatis及最佳实践

(本笔记写于2010年底)

 

2010年4月份 iBATIS团队发布iBATIS 3.0的GA版本的候选版本,在iBATIS 3中引入了泛型、注解支持等,因此需要Java5+才能使用,但在2010年6月16日,iBATIS团队决定从apache迁出并迁移到Google Code,并更名为MyBatis。目前新网站上文档并不完善。

 

目前iBATIS 2.x和MyBatis 3不是100%兼容的,如配置文件的DTD变更,SqlMapClient直接由SqlSessionFactory代替了,包名也有com.ibatis变成org.ibatis等等。

ibatis 3.x和MyBatis是兼容的,只需要将DTD变更一下就可以了。

 

感兴趣的朋友可以到http://www.mybatis.org/官网去下载最新的文档学习,作者只使用过iBATIS2.3.4及以前版本,没在新项目使用过最新的iBATIS 3.x和Mybatis,因此如果读者需要在项目中使用最新的MyBatis,请先做好调研再使用。

 

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

 

1、准备需要的jar包,到MyBatis官网下载mybatis 3.0.4版本和mybatis-spring 1.0.0版本,并拷贝如下jar包到类路径:


mybatis-3.0.4\mybatis-3.0.4.jar              //核心MyBatis包

mybatis-spring-1.0.0\mybatis-spring-1.0.0.jar  //集成Spring包

 

2、对象模型定义,此处使用第七章中的UserModel;

 

3、MyBatis映射定义(chapter8/sqlmaps/UserSQL-mybatis.xml):


java代码:
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  3.      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  4. <mapper namespace="UserSQL">  
  5.     <sql id="createTable">  
  6.     <!--id自增主键从0开始 -->  
  7.     <![CDATA[  
  8.       create memory table test(  
  9.         id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,   
  10.         name varchar(100))  
  11.     ]]>  
  12.     </sql>  
  13.     <sql id="dropTable">  
  14.     <![CDATA[ drop table test ]]>  
  15.     </sql>  
  16.     <insert id="insert" parameterType="cn.javass.spring.chapter7.UserModel">  
  17.     <![CDATA[ insert into test(name) values (#{myName}) ]]>  
  18.         <selectKey resultType="int" keyProperty="id" order="AFTER">  
  19.             <!-- 获取hsqldb插入的主键 -->  
  20.             call identity();  
  21.             <!-- mysql使用select last_insert_id();获取插入的主键 -->  
  22.         </selectKey>  
  23.     </insert>  
  24.     <select id="countAll" resultType="java.lang.Integer">  
  25.     <![CDATA[ select count(*) from test ]]>     
  26.     </select>  
  27. </mapper>  
  28.    
  29.    

     从映射定义中可以看出MyBatis与iBATIS2.3.4有如下不同:

  • http://ibatis.apache.org/dtd/sql-map-2.dtd 废弃,而使用http://mybatis.org/dtd/mybatis-3-mapper.dtd。
  • <sqlMap>废弃,而使用<mapper>标签;
  • <statement>废弃了,而使用<sql>标签;
  • parameterClass属性废弃,而使用parameterType属性;
  • resultClass属性废弃,而使用resultType属性;
  • #myName#方式指定命名参数废弃,而使用#{myName}方式。

 

3、 MyBatis配置文件(chapter8/sql-map-config-mybatis.xml)定义:

 

java代码:
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  3.     "http://mybatis.org/dtd/mybatis-3-config.dtd">  
  4. <configuration>  
  5.     <settings>  
  6.         <setting name="cacheEnabled" value="false"/>  
  7.     </settings>  
  8.     <mappers>  
  9.         <mapper resource="chapter8/sqlmaps/UserSQL-mybatis.xml"/>  
  10.     </mappers>  
  11. </configuration>  

     从配置定义中可以看出MyBatis与iBATIS2.3.4有如下不同:

  • http://ibatis.apache.org/dtd/sql-map-config-2.dtd废弃,而使用http://mybatis.org/dtd/mybatis-3-config.dtd;
  • < sqlMapConfig >废弃,而使用<configuration>;
  • settings属性配置方式废弃,而改用子标签< setting name=".." value=".."/>方式指定属性,且一些属性被废弃,如maxTransactions;
  • < sqlMap>废弃,而采用<mappers>标签及其子标签<mapper>定义。

 

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

 

5、 定义Dao接口实现,此处是MyBatis实现:

 

java代码:
Java代码  收藏代码
  1. package cn.javass.spring.chapter8.dao.mybatis;  
  2. //省略import  
  3. public class UserMybatisDaoImpl extends SqlSessionDaoSupport  
  4. implements IUserDao {  
  5.     @Override  
  6.     public void save(UserModel model) {  
  7.         getSqlSession().insert("UserSQL.insert", model);  
  8.     }  
  9.     @Override  
  10.     public int countAll() {  
  11.         return (Integer) getSqlSession().selectOne("UserSQL.countAll");  
  12.     }  
  13. }  
  14.    

 

     和Ibatis集成方式不同的有如下地方:

  • 使用SqlSessionDaoSupport来支持一致性的DAO访问,该类位于org.mybatis.spring.support包中,非Spring提供;
  • 使用getSqlSession方法获取SqlSessionTemplate,在较早版本中是getSqlSessionTemplate方法名,不知为什么改成getSqlSession方法名,因此这个地方在使用时需要注意。
  •  SqlSessionTemplate是SqlSession接口的实现,并且自动享受Spring管理事务好处,因此从此处可以推断出为什么把获取模板类的方法名改为getSqlSession而不是getSqlSessionTemplate。

6、进行资源配置,使用resources/chapter7/applicationContext-resources.xml:

 

7、dao定义配置,在chapter8/applicationContext-mybatis.xml中添加如下配置:

 

java代码:
Java代码  收藏代码
  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
  2.   <property name="dataSource" ref="dataSource"/><!-- 1、指定数据源 -->  
  3.   <property name="configLocation" value="chapter8/sql-map-config-mybatis.xml"/>  
  4. </bean>  
  5. <bean id="abstractDao" abstract="true">  
  6.    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>  
  7. </bean>     
  8. <bean id="userDao"  
  9.            class="cn.javass.spring.chapter8.dao.mybatis.UserMybatisDaoImpl"  
  10.            parent="abstractDao"/>   

 

   和Ibatis集成方式不同的有如下地方:

  • SqlMapClient类废弃,而使用SqlSessionFactory代替;
  • 使用SqlSessionFactoryBean进行集成MyBatis。

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

 

 

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


java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testMybatisBestPractice() {  
  3.     String[] configLocations = new String[] {  
  4.         "classpath:chapter7/applicationContext-resources.xml",  
  5.         "classpath:chapter8/applicationContext-mybatis.xml"};  
  6.      ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);  
  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. }  
  13.    
  14.    


和Spring 集成Ibatis的最佳实践完全一样,除了使用applicationContext-mybatis.xml代替了applicationContext-ibatis.xml,其他完全一样,且MyBatis 3.x与Spring整合只能运行在Spring3.x。

 

在写本书时,MyBatis与Spring集成所定义的API不稳定,且期待Spring能在发布新版本时将加入对MyBatis的支持。

 


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. <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">  
  2.     <property name="persistenceUnitName" value="persistenceUnit"/>  
  3. </bean>  
  • 从JNDI中获取:用于从Java EE服务器获取指定的EntityManagerFactory,这种方式在进行Spring事务管理时一般要使用JTA事务管理;

      使用方式:

 

java代码:
Java代码  收藏代码
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns:jee="http://www.springframework.org/schema/jee"  
  4.     xsi:schemaLocation="  
  5.        http://www.springframework.org/schema/beans  
  6.        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.        http://www.springframework.org/schema/jee  
  8.        http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">  
  9.   <jee:jndi-lookup id="entityManagerFactory"  jndi-name="persistence/persistenceUnit"/>  
  10. </beans>  

 

此处需要使用“jee”命名标签,且使用<jee:jndi-lookup>标签进行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. <?xml version="1.0" encoding="UTF-8"?>  
  2. <persistence version="1.0"  
  3.     xmlns="http://java.sun.com/xml/ns/persistence"  
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence                      http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">  
  6.     <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL"/>  
  7. </persistence>  
  8.    

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

 

2、 数据源定义,此处使用第7章的配置文件,即“chapter7/applicationContext-resources.xml”文件。

 

3、 EntityManagerFactory配置定义(chapter8/applicationContext-jpa.xml):

 

java代码:
Java代码  收藏代码
  1. <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">  
  2.   <property name="dataSource" ref="dataSource"/>  
  3.   <property name="persistenceXmlLocation" value="chapter8/persistence.xml"/>  
  4.   <property name="persistenceUnitName" value="persistenceUnit"/>  
  5.   <property name="persistenceProvider" ref="persistenceProvider"/>  
  6.   <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>  
  7.   <property name="jpaDialect" ref="jpaDialect"/>  
  8.   <property name="jpaProperties">  
  9.       <props>  
  10.           <prop key="hibernate.show_sql">true</prop>  
  11.       </props>  
  12.   </property>  
  13. </bean>  
  14. <bean id="persistenceProvider" class="org.hibernate.ejb.HibernatePersistence"/>  

 

 

java代码:
Java代码  收藏代码
  1. <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">  
  2.    <property name="generateDdl" value="false" />  
  3.    <property name="database" value="HSQL"/>  
  4. </bean>  
  5. <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>  
  • 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(configLocations);  
  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 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " + "name varchar(100))";  
  5.    executeSql(createTableSql);  
  6. }  
  7. @After  
  8. public void tearDown() throws SQLException {  
  9.     String dropTableSql = "drop table test";  
  10.     executeSql(dropTableSql);  
  11. }  
  12.   
  13. private 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. private void closeEntityManager(EntityManager em) {  
  21.   em.close();  
  22. }  
  23. private void rollbackTransacrion(EntityManager em) throws SQLException {  
  24.   if(em != null) {  
  25.      em.getTransaction().rollback();  
  26.   }           
  27. }  
  28. private void commitTransaction(EntityManager em) throws SQLException {  
  29.    em.getTransaction().commit();  
  30. }  
  31. private void beginTransaction(EntityManager em) throws SQLException {  
  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. <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
  2.     <property name="entityManagerFactory" ref="entityManagerFactory"/>  
  3. </bean>  
  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(entityManagerFactory);  
  4.   final UserModel2 model = new UserModel2();  
  5.   model.setMyName("test1");  
  6.   PlatformTransactionManager txManager = ctx.getBean(PlatformTransactionManager.class);  
  7.   new TransactionTemplate(txManager).execute(  
  8.     new TransactionCallback<Void>() {  
  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.TransactionRequiredException: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 IUserDao {  
  5.     private static final String COUNT_ALL_JPAQL = "select count(*) from 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. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xmlns:tx="http://www.springframework.org/schema/tx"  
  5.     xsi:schemaLocation="  
  6.        http://www.springframework.org/schema/beans  
  7.        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  8.        http://www.springframework.org/schema/tx  
  9.        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  

 

4.2、为@Transactional注解事务开启事务支持:

 

 

java代码:
Java代码  收藏代码
  1. <tx:annotation-driven transaction-manager="txManager"/>  

只为类添加@Transactional 注解是不能支持事务的,需要通过<tx:annotation-driven>标签来开启事务支持,其中txManager属性指定事务管理器。

 

 

4.3、配置DAO Bean:

 

java代码:
Java代码  收藏代码
  1. <bean id="abstractDao" abstract="true">  
  2.     <property name="entityManagerFactory" ref="entityManagerFactory"/>  
  3. </bean>     
  4. <bean id="userDao"  
  5.       class="cn.javass.spring.chapter8.dao.jpa.UserJpaDaoImpl"  
  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(configLocations);  
  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(PlatformTransactionManager.class);  
  4.     final JpaTemplate jpaTemplate = new JpaTemplate(entityManagerFactory);  
  5.     TransactionTemplate tansactionTemplate = new TransactionTemplate(txManager);  
  6.     tansactionTemplate.execute(new TransactionCallback<Void>() {  
  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方式一样简洁。

 

 

本文借鉴:【http://sishuok.com/forum/blogPost/list/0/2500.html】