1. 從方法調用到事件處理
在hibernate當中,大部分操作最終都是轉化爲事件,然後由對應的事件處理函數來處理。而事件內部主要包含的就是對Session實例的引用
2. 數據加載
數據加載主要在LoadEventListener的doLoad()內部完成。doLoad在加載數據時,會查詢他的兩級緩存。當在緩存當中找不到時,纔會進行實際的數據庫操作。
2.1. loadFromSessionCache
Session的緩存主要是指他的PersistenceContext,每次當SessionFactory要創建一個新的Session時,他都會爲其創建一個新的PersistenceContext實例。一般情況下,Session的生命週期都非常短,所以PersistenceContext作爲緩存的作用並不明顯。但是在Web開發當中,我們經常會在一個Long Conversation中重用同一個Session,此時,PersistenceContext作爲緩存的意義將會變得重要。
如果我們在PersistenceContext中找不到所需的實例,則他將會通過Session所關聯的Interceptor來獲取實例,這裏就給了我們一個繞開數據庫注入實例的機會。
2.1.1. 代碼
public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
final Object result = persistenceContext.getEntity(key);
if ( result == null ) {
final Object newObject = interceptor.getEntity( key.getEntityName(), key.getIdentifier() ); if ( newObject != null ) {
lock( newObject, LockMode.NONE );
}
return newObject;
}else {
return result;
}
}
注:這段代碼是主要的數據加載部分,從代碼可以看到在加載數據時,首先調用的是
persistenceContext.getEntity(),當失敗時則調用interceptor.getEntity()。
2.2. loadFromSecondLevelCache
如果我們在hibernate的配置文件中啓用了cache,那麼在這裏他將會查詢二級緩存。查詢Cache的過程比較簡單,首先是生成一個CacheKey,並根據這個來查詢。
2.2.1. 代碼
protected Object loadFromSecondLevelCache(
final LoadEvent event,
final EntityPersister persister,
final LoadEventListener.LoadType options) throws HibernateException {
final SessionImplementor source = event.getSession();
final boolean useCache = persister.hasCache() &&
source.getCacheMode().isGetEnabled() &&
event.getLockMode().lessThan(LockMode.READ);
if (useCache) {
final SessionFactoryImplementor factory = source.getFactory();
final CacheKey ck = new CacheKey(
event.getEntityId(),
persister.getIdentifierType(),
persister.getRootEntityName(),
source.getEntityMode(),
source.getFactory()
);
Object ce = persister.getCache()
.get( ck, source.getTimestamp() );
……
if ( ce != null ) {
CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure()
.destructure(ce, factory);
// Entity was found in second-level cache...
return assembleCacheEntry(
entry,
event.getEntityId(),
persister,
event
);
}
}
return null;
}
注:在操作二級緩存時,Hibernate並不是直接使用Object的ID或者hashCode來作爲Key,而是使用一個自
定義的CacheKey類。而所存儲的對象也不僅僅是實體本身,而是一個經過assemble的對象。
2.3. loadFromDatasource
這是loadFromDataSource的大致流程,主要的操作都是在類UniqueEntityLoader內部完成,前半部分主要是生成JDBC的PreparedStatement,並進行數據庫查詢。當結果返回以後,需要把結果先放到Session的PersistenceContext當中,這通過TwoPhaseLoad的addUninitializedEntity()和postHydrate()完成。前一個方法主要是存入一個實例,該實例只設置了id屬性。後續的EntityPersister.hydrate(),用於獲取其他的屬性值,當所有屬性值獲取以後將調用postHydrate()方法更新屬性值。在存入PersistenceContext以後,要操作的就是二級緩存,這通過TwoPhaseLoad.initializeEnttiy()完成。
3. 總結:
在整個Hibernate的get操作裏面,我們可以通過提供不同的Interceptor,或者直接操作他的二級緩存來改變他的整個查詢策略。
瞭解了這點,可以部分改進我們的數據庫測試。當我們的某些操作僅僅依賴於get操作時,我們可以把測試數據直接通過Interceptor或者二級緩存壓入Hiberante,而不用真正的寫入數據庫。