在分析HibernateTemplate前,首先從網上了解下一些關於回調的一些概念。我相信在瞭解其原理實現的基礎上,可以更好的進行開發和擴展,關鍵得理解其思想。
軟件模塊之間總是存在着一定的接口,從調用方式上,可以把他們分爲三類:同步調用、回調和異步調用。
同步調用是一種阻塞式調用,調用方要等待對方執行完畢才返回,它是一種單向調用。(簡單來說就是順序執行啦。)
回調是一種雙向調用模式,也就是說,被調用方在接口被調用時也會調用對方的接口。(個人覺得有點像模板方法的實現。)
異步調用是一種類似消息或事件的機制,不過它的調用方向剛好相反,接口的服務在收到某種訊息或發生某種事件時,會主動通知客戶方(即調用客戶方的接口)。(差不多就是異步執行吧。)
回調和異步調用的關係非常緊密,通常我們使用回調來實現異步消息的註冊,通過異步調用來實現消息的通知。同步調用是三者當中最簡單的,而回調又常常是異步調用的基礎。
由於本文章主要關注的是Java 的回調機制,所以其他就不介紹了。下面來看看Java 是怎麼來實現回調機制的。
package callback;
/**
*
* @author zhxing
* @since 2010.4.16
*
*/
public class Test{
private Callback callback;
public void setCallback(Callback callback){
this.callback=callback;
}
//實際方法
public void foo(){
System.out.println("this is foo();");
//在這裏調用回調函數
if(callback!=null){
callback.doSomething();
}
}
//測試
public static void main(String[] args) {
Test t=new Test();
t.setCallback(new Callback(){
public void doSomething() {
System.out.println("this is callback.");
}
});
t.foo();
}
}
//回調函數
interface Callback {
void doSomething();
}
一般在我們數據庫開發中,都會有打開和關閉數據、捕獲異常等的一些固定的操作,寫久了也就特別的反感了。是吧。。下面來利用回調簡化數據庫操作,例子很簡單,對於數據的打開和關閉我只做了簡單的描述。
package callback;
public class UserDao {
private UserTemplate template=new UserTemplate();
//這個實際上也是用回調方法,不過在UserTemplate 進行了封裝
public void add(final String name){
this.template.add(name);
}
//這個是用回調方法來實現的
public String getName(){
return (String)this.template.execute(new UserCallback(){
public Object doSomething() {
System.out.println("成功讀取數據庫。。。name= zhxing");
return "zhxing";
}
});
}
//測試
public static void main(String[] args) {
UserDao u=new UserDao();
u.add("zhxing");
System.out.println(u.getName());
}
}
package callback;
//回調函數的實現
public class UserTemplate {
//調用回調函數,這裏是不是有點像模板方法了,呵呵
public Object execute(UserCallback callback){
System.out.println("打開數據庫連接");
Object o=callback.doSomething();
System.out.println("關閉數據庫連接");
return o;
}
//這裏用來簡化常用的操作
public void add(final String name){
execute(new UserCallback(){
public Object doSomething() {
System.out.println("name="+name+" 成功加入數據庫。。。");
return null;
}
});
}
}
package callback;
//回調函數接口
public interface UserCallback {
Object doSomething();
}
好了,進入正題吧。。來研究下Spring 中HibernateTemplate 是怎麼實現了。。其實上面的那個例子已經可以說明全部了,呵呵。。下面我們按照開發的流程來進行一些源碼分析。
當我們用Spring+Hibernate實現Dao層的時候,一般會去繼承HibernateDaoSupport這個類。
來分析下這個HibernateDaoSupport類(具體方法細節請查看源碼):
public abstract class HibernateDaoSupport extends DaoSupport {
private HibernateTemplate hibernateTemplate;
//Set the Hibernate SessionFactory to be used by this DAO.
public final void setSessionFactory(SessionFactory sessionFactory) {
}
protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) {
}
//返回這個HiberanteTemplate 中的成員變量SessionFactory
public final SessionFactory getSessionFactory() {
}
public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
public final HibernateTemplate getHibernateTemplate() {
return this.hibernateTemplate;
}
//檢查HibernateTemplate是否爲空,如果類中的HibernateTemplate未設置則會報錯
protected final void checkDaoConfig() {
if (this.hibernateTemplate == null) {
throw new IllegalArgumentException("'sessionFactory' or 'hibernateTemplate' is required");
}
}
//獲得Hibernate中的session,可以在HibernateTemplate中配置是否在Session沒創建時新創建一個,在事務處理中很有用
protected final Session getSession()
throws DataAccessResourceFailureException, IllegalStateException {
return getSession(this.hibernateTemplate.isAllowCreate());
}
//這裏是獲得Hibernate 的session 的主要方法
protected final Session getSession(boolean allowCreate)
throws DataAccessResourceFailureException, IllegalStateException {
return (!allowCreate ?
SessionFactoryUtils.getSession(getSessionFactory(), false) :
SessionFactoryUtils.getSession(
getSessionFactory(),
this.hibernateTemplate.getEntityInterceptor(),
this.hibernateTemplate.getJdbcExceptionTranslator()));
}
//把HibernateException異常轉換成DataAccessException異常
protected final DataAccessException convertHibernateAccessException(HibernateException ex) {
return this.hibernateTemplate.convertHibernateAccessException(ex);
}
//釋放Hibernate 的session
protected final void releaseSession(Session session) {
SessionFactoryUtils.releaseSession(session, getSessionFactory());
}
}
從上面可知道,HibernateDaoSupport 的主要作用是獲取Hibernate 的Session (如果直接用HibernateTemplate是沒有辦法獲得Session 的)和管理HibernateTemplate。在Spring 中可以對直接繼承該類,然後在Dao 的配置上就可以直接進行注入SessionFactory了,還可以直接從Dao中獲取Hibernate的Session進行操作。
現在回到正題,進行分析下HibernateTemplate,先看下他的成員變量有哪些。
public class HibernateTemplate extends HibernateAccessor implements HibernateOperations {
//是否在Session不存在的時候進行創建
private boolean allowCreate = true;
//是否每次都生成新的Session
private boolean alwaysUseNewSession = false;
//是不是用原生的Hibernate 的session而不是用它的代理
private boolean exposeNativeSession = false;
//檢查Hibernate 的session 是否爲只讀模式
private boolean checkWriteOperations = true;
//緩存Query
private boolean cacheQueries = false;
//設置Query緩存的名稱
private String queryCacheRegion;
//設置批量獲取的大小
private int fetchSize = 0;
//設置最大的數據集
private int maxResults = 0;
}
HibernateTemplate 封裝了很多Hibernate 的原始的操作,但把Hibernate 的session設置爲了protected,而不能在它的子類外進行獲取。下面來看看我們最常用get 和save 方法。
public Object get(Class entityClass, Serializable id) throws DataAccessException {
return get(entityClass, id, null);
}
//實際get調用這個方法,增加了一個鎖模式。
public Object get(final Class entityClass, final Serializable id, final LockMode lockMode)
throws DataAccessException {
//這裏就是我們上面討論的回調方法了。。
return executeWithNativeSession(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
if (lockMode != null) {
return session.get(entityClass, id, lockMode);
}
else {
return session.get(entityClass, id);
}
}
});
}
public Serializable save(final Object entity) throws DataAccessException {
//這裏也是用了回調,和上面一樣的。
return (Serializable) executeWithNativeSession(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
checkWriteOperationAllowed(session);
return session.save(entity);
}
});
}
下面來看看這個回調函數的方法。
protected Object doExecute(HibernateCallback action, boolean enforceNewSession, boolean enforceNativeSession)
throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
//判斷是否強制新建Session
Session session = (enforceNewSession ?
SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession());
//判斷是否設置了事務且是否強制新建session
boolean existingTransaction = (!enforceNewSession &&
(!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory())));
if (existingTransaction) {
logger.debug("Found thread-bound Session for HibernateTemplate");
}
FlushMode previousFlushMode = null;
try {
//設置並返回FlushMode
previousFlushMode = applyFlushMode(session, existingTransaction);
//設置過濾器
enableFilters(session);
//是否創建session 代理
Session sessionToExpose =
(enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session));
//這裏調用回調函數了
Object result = action.doInHibernate(sessionToExpose);
flushIfNecessary(session, existingTransaction);
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (SQLException ex) {
throw convertJdbcAccessException(ex);
}
catch (RuntimeException ex) {
// Callback code threw application exception...
throw ex;
}
finally {
if (existingTransaction) {
logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
disableFilters(session);
if (previousFlushMode != null) {
session.setFlushMode(previousFlushMode);
}
}
else {
// Never use deferred close for an explicitly new Session.
if (isAlwaysUseNewSession()) {
SessionFactoryUtils.closeSession(session);
}
else {
//把session綁定到線程變量中(ThreadLocal) SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
}
}
}
}
到這裏基本就簡單分析完了,關於HibernateTemplate的其他操作,其實都差不多的,對Hibernate的一些常用操作進行了回調封裝,可以自己分析下。