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,而不用真正的写入数据库。