1. 程式人生 > >Spring對ORM的支援之整合Hibernate3

Spring對ORM的支援之整合Hibernate3

Hibernate是全自動的ORM框架,能自動為物件生成相應SQL並透明的持久化物件到資料庫。

Spring2.5+ 版本支援Hibernate 3.1+ 版本,不支援低版本,Spring 3.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吧:

、準備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程式碼:
package cn.javass.spring.chapter7;
public class UserModel {
    private int id;
    private String myName;
    //省略getter和setter
}

3、 Hibernate 對映定義(chapter8/hbm/user.hbm.xml ),定義物件和資料庫之間的對映:

java程式碼:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.javass.spring.chapter7.UserModel" table="test">
        <id name="id" column="id"><generator class="native"/></id>
        <property name="myName" column="name"/>
    </class>
</hibernate-mapping>

4、    資料來源定義,此處使用第7 章的配置檔案,即“chapter7/ applicationContext-resources.xml ”檔案。

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

java程式碼:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/> <!-- 指定資料來源 -->
      <property name="mappingResources">     <!-- 指定對映定義 -->
        <list>
          <value>chapter8/hbm/user.hbm.xml</value>
        </list>
      </property>
      <property name="hibernateProperties">   <!--指定Hibernate屬性 -->
        <props>
          <prop key="hibernate.dialect">
              org.hibernate.dialect.HSQLDialect
          </prop>
        </props>
      </property>
</bean>

6、    獲取SessionFactory 

java程式碼:

package cn.javass.spring.chapter8;
//省略import
public class HibernateTest {
    private static SessionFactory sessionFactory;
    @BeforeClass
    public static void beforeClass() {
        String[] configLocations = new String[] {
                "classpath:chapter7/applicationContext-resources.xml",
                "classpath:chapter8/applicationContext-hibernate.xml"};
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);
        sessionFactory = ctx.getBean("sessionFactory", SessionFactory.class);
}
}

此處我們使用了chapter7/applicationContext-resources.xml定義的“dataSource”資料來源,通過ctx.getBean(“sessionFactory”, SessionFactory.class)獲取SessionFactory。

7、 通過SessionFactory 獲取Session 物件進行建立和刪除表:

java程式碼:
@Before
public void setUp() {
  //id自增主鍵從0開始
  final String createTableSql = "create memory table test" + "(id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " + "name varchar(100))";
  sessionFactory.openSession().
  createSQLQuery(createTableSql).executeUpdate();
}
@After
public void tearDown() {
    final String dropTableSql = "drop table test";
    sessionFactory.openSession().
    createSQLQuery(dropTableSql).executeUpdate();
}

使用SessionFactory建立Session,然後通過Session物件的createSQLQuery建立本地SQL執行建立和刪除表。

8、 使用SessionFactory 獲取Session 物件進行持久化資料:

java程式碼:
@Test
public void testFirst() {
    Session session = sessionFactory.openSession();
    Transaction transaction = null;
    try {
        transaction = beginTransaction(session);
        UserModel model = new UserModel();
        model.setMyName("myName");
        session.save(model);
    } catch (RuntimeException e) {
        rollbackTransaction(transaction);
        throw e;
    } finally {
        commitTransaction(session);
    }
}
java程式碼:
private Transaction beginTransaction(Session session) {
    Transaction transaction = session.beginTransaction();
    transaction.begin();
    return transaction;
}
private void rollbackTransaction(Transaction transaction) {
   if(transaction != null) {
        transaction.rollback();
    }
}
private void commitTransaction(Session session) {
    session.close();
}

使用SessionFactory獲取Session進行操作,必須自己控制事務,而且還要保證各個步驟不會出錯,有沒有更好的解決方案把我們從程式設計事務中解脫出來?Spring提供了HibernateTemplate模板類用來簡化事務處理和常見操作。

8.2.2 使用HibernateTemplate

HibernateTimplate模板類用於簡化事務管理及常見操作,類似於JdbcTemplate模板類,對於複雜操作通過提供HibernateCallback回撥介面來允許更復雜的操作。

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

java程式碼:
@Test
public void testHibernateTemplate() {
HibernateTemplate hibernateTemplate =
new HibernateTemplate(sessionFactory);
    final UserModel model = new UserModel();
    model.setMyName("myName");
    hibernateTemplate.save(model);
    //通過回撥允許更復雜操作
    hibernateTemplate.execute(new HibernateCallback<Void>() {
        @Override
        public Void doInHibernate(Session session)
            throws HibernateException, SQLException {
            session.save(model);
            return null;
        }});
}

通過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程式碼:
package cn.javass.spring.chapter8.dao.hibernate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import cn.javass.spring.chapter7.UserModel;
import cn.javass.spring.chapter7.dao.IUserDao;
public class UserHibernateDaoImpl extends HibernateDaoSupport implements IUserDao {
    private static final String COUNT_ALL_HQL = "select count(*) from UserModel";
    @Override
    public void save(UserModel model) {
        getHibernateTemplate().save(model);
    }
    @Override
    public int countAll() {
        Number count = (Number) getHibernateTemplate().find(COUNT_ALL_HQL).get(0);
        return count.intValue();
    }
}

此處注意首先Hibernate實現放在dao.hibernate包裡,其次實現類命名如UserHibernateDaoImpl,即×××HibernateDaoImpl,當然如果自己有更好的命名規範可以遵循自己的,此處只是提個建議。

3 、進行資源配置,使用resources/chapter7/applicationContext-resources.xml 

、dao 定義配置,在chapter8/applicationContext-hibernate.xml 中新增如下配置:

java程式碼:
<bean id="abstractDao" abstract="true">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>   
<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程式碼:
@Test
public void testBestPractice() {
    String[] configLocations = new String[] {
            "classpath:chapter7/applicationContext-resources.xml",
            "classpath:chapter8/applicationContext-hibernate.xml"};
    ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);
    IUserDao userDao = ctx.getBean(IUserDao.class);
    UserModel model = new UserModel();
    model.setMyName("test");
    userDao.save(model);
    Assert.assertEquals(1, userDao.countAll());
}

和Spring JDBC框架的最佳實踐完全一樣,除了使用applicationContext-hibernate.xml代替了applicationContext-jdbc.xml,其他完全一樣。也就是說,DAO層的實現替換可以透明化。

8.2.4  Spring+Hibernate的CRUD

Spring+Hibernate CRUD(增刪改查)我們使用註解類來示例,讓我們看具體示例吧:

、首先定義帶註解的模型物件UserModel2 

  • 使用JPA註解@Table指定表名對映;
  • 使用註解@Id指定主鍵對映;
  • 使用註解@ Column指定資料庫列對映;
java程式碼:
package cn.javass.spring.chapter8;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "test")
public class UserModel2 {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Column(name = "name")
    private String myName;
    //省略getter和setter
}

2、  定義配置檔案(chapter8/applicationContext-hibernate2.xml ):

2.1、         定義SessionFactory 

此處使用AnnotationSessionFactoryBean通過annotatedClasses屬性指定註解模型來定義對映元資料;

java程式碼:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>  <!-- 1、指定資料來源 -->
  <property name="annotatedClasses">           <!-- 2、指定註解類 -->
     <list><value>cn.javass.spring.chapter8.UserModel2</value></list>
</property>
  <property name="hibernateProperties"><!-- 3、指定Hibernate屬性 -->
    <props>
     <prop key="hibernate.dialect">
        org.hibernate.dialect.HSQLDialect
     </prop>
    </props>
  </property>
</bean>

2.2、 定義HibernateTemplate  

java程式碼:
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

、最後進行CURD 測試吧:

java程式碼:
@Test
public void testCURD() {
    String[] configLocations = new String[] {
            "classpath:chapter7/applicationContext-resources.xml",
            "classpath:chapter8/applicationContext-hibernate2.xml"};
    ApplicationContext ctx =  new ClassPathXmlApplicationContext(configLocations);
    HibernateTemplate hibernateTemplate =  ctx.getBean(HibernateTemplate.class);
    UserModel2 model = new UserModel2();
    model.setMyName("test");
    insert(hibernateTemplate, model);
    select(hibernateTemplate, model);
    update(hibernateTemplate, model);
    delete(hibernateTemplate, model);
}

private void insert(HibernateTemplate hibernateTemplate, UserModel2 model) {
    hibernateTemplate.save(model);
}
private void select(HibernateTemplate hibernateTemplate, UserModel2 model) {
    UserModel2 model2 = hibernateTemplate.get(UserModel2.class, 0);
    Assert.assertEquals(model2.getMyName(), model.getMyName());
    List<UserModel2> list = hibernateTemplate.find("from UserModel2");
    Assert.assertEquals(list.get(0).getMyName(), model.getMyName());
}
private void update(HibernateTemplate hibernateTemplate, UserModel2 model) {
    model.setMyName("test2");
    hibernateTemplate.update(model);
}
private void delete(HibernateTemplate hibernateTemplate, UserModel2 model) {
    hibernateTemplate.delete(model);
}

Spring整合Hibernate進行增刪改查是不是比Spring JDBC方式簡單許多,而且支援註解方式配置對映元資料,從而減少對映定義配置檔案數量。