Hibernate與Spring整合後,就可以使用IoC及AOP的功能了,好處不在多言。另外一個好處,就是可以通過使用Spring的HibernateTemplate來簡化數據庫CRUD代碼。然而,正是這個HibernateTemplate,存在着以下的缺點:
一是功能不全,不如Hibernate的儲如createQuery()等方法方便、靈活與強大,使用頗受限制;
二是HibernateTemplate中的SessionFacotry封裝得太死,且session常常會自動過早關閉,使用上頗多不便;
三是Spring1.2.7實際上只支持Hibernate3.0.5,HibernateTemplate無法使用Hibernate3.1以後新加的功能。
正是由於這三點,使我抵制住了使用HibernateTemplate的誘惑,在將Spring與Hibernate整合後,通過簡單的配置,在Spring程序中自由地使用Hibernate3.1.2的功能。這樣既可以實現強強聯手,又可以在Hibernate新版本出來後,馬上進行重新整合,無需等着新版的Spring出來。
在實現上,主要有三點。
一是在Spring的配置文件中,無需定義長長的hibernateTemplate,只需定義sessionFacotry就行了。
例如,設有一"組織DaoImpl",實現了"組織Dao"及"HibernateDao"兩個接口。而"組織Service"通過將"組織DaoImpl"封裝起來,對外提供相應的數據庫功能服務。
public interface HibernateDao {
public void setSessionFactory(SessionFactory sessionFactory);
}
public interface 組織Dao {
public void 增加組織(組織 組織);
public void 刪除組織(Long 組織編號);
public 組織 get組織By名稱(String 組織名稱);
......
}
public class 組織DaoImpl implements 組織Dao, HibernateDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public 組織 get組織By名稱(String 組織名稱) {
......
}
}
public interface 組織Service {
public 組織 get組織By名稱(String 組織名稱);
......
}
(實際代碼中,組織Service與組織DaoI的接口是一樣的)
先在Spring的配置文件中定義"組織Service"與"組織Dao":
<bean id="組織Service"
class="com.sarkuya.service.組織ServiceImpl">
<property name="組織Dao">
<ref bean="組織Dao" />
</property>
</bean>
<bean id="組織Dao"
class="com.sarkuya.model.dao.hibernate.組織DaoImpl">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
這一步比較好理解。問題是如何將我們所需的sessionFactory傳過來。Hibernate的sessionFactory不是一個可以用構造方法或setter方法就可以直接生成的類,而是需要進行一定的"運算"後才得出的類,典型的形式如:
sessionFactory = new Configuration().buildSessionFactory();
因此,簡單地在Spring中如下配置行不通。
<bean id="sessionFactory"
class="org.hibernate.SessionFactory">
<property name="xxx">
<value>xxx</valu>
</property>
</bean>
所幸,Spring提供了一種可定義由工廠方法所產生的bean的方式。
<bean id="sessionFactory"
class="com.sarkuya.hibernate.util.HibernateUtil"
factory-method="getSessionFactory" />
這種方式,當我們需要得到sessionFacotry的bean時,Spring就會從com.sarkuya.hibernate.util.HibernateUtil的getSessionFactory()方法中獲得。這種方式,我們最熟悉不過了:
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
sybaseSessionFactory = new Configuration()
.addClass(com.sarkuya.model.cfg.hibernate.組織.class)
.configure("/hibernate_hsql.cfg.xml")
.buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
好了,以上步驟,我們已經成功地將sessionFactory注入組織Dao中,再注入組織Service中,根據測試先行原則,看看如何在TestCase中獲取組織Service這個bean。
二,在代碼中獲取組織Service。
public class 組織ServiceTest extends TestCase {
private 組織Service 組織Service;
protected void setUp() throws Exception {
String[] configLocation = new String[] {
"/web/WEB-INF/training-service.xml",
"/web/WEB-INF/training-data.xml"
};
ApplicationContext ac = new FileSystemXmlApplicationContext(configLocation);
組織Service = (組織Service)ac.getBean("組織Service");
}
public void testGet組織By名稱() {
System.out.println("get組織By名稱");
組織 組織 = 組織Service.增加組織(new 組織("aaa"));
組織 = 組織Service.get組織By名稱("aaa");
assertEquals("aaa", 組織.get名稱());
組織 = 組織Service.get組織By名稱("abc");
assertNull(組織);
}
}
三,組織DaoImpl的代碼:
/*
* @param 組織名稱
* @return 組織 或者 null
*/
public 組織 get組織By名稱(String 組織名稱) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction(); //Hiberante3.1以後的代碼
組織 組織 = (組織)session.createQuery("from 組織 c where c.名稱 = '" + 組織名稱 + "'")
.uniqueResult();
session.getTransaction().commit();
return 組織;
}
由上可見,Hibernate的核心代碼一點未變,從而在與Spring整合的基礎上,實現了與Spring的解耦。