本文基于Hibernate 5.2
LoadEventListener.LoadType类
众所周知Session中不同的查询方式在细节上有所区别,如Session.get()查找缓存和数据库,而Session.load()则查找缓存,如果缓存没有该实体则返回一个代理,在使用实体其他字段时才查找数据库。
LoadType类定义了这些区别。
public interface LoadEventListener extends Serializable {
public static final LoadType GET = new LoadType( "GET" )
.setAllowNulls( true )
.setAllowProxyCreation( false )
.setCheckDeleted( true )
.setNakedEntityReturned( false );
public static final LoadType LOAD = new LoadType( "LOAD" )
.setAllowNulls( false )
.setAllowProxyCreation( true )
.setCheckDeleted( true )
.setNakedEntityReturned( false );
//其他类型的LoadType
public static final class LoadType {
private String name;
private boolean nakedEntityReturned;
private boolean allowNulls;
private boolean checkDeleted;
private boolean allowProxyCreation;
}
//其他代码
}
Session查询
1.Session生成LoadEvent和LoadType
2.通知LoadListener
3.LoadListenr根据LoadType进行查找和返回结果
1.生成LoadEvent和LoadType
在源码中,load与get方法都是委托给一个内部类IdentifierLoadAccessImpl来实现的
- SessionImpl#load
public <T> T load(Class<T> entityClass, Serializable id) throws HibernateException {
return this.byId( entityClass ).getReference( id );
}
- SessionImpl#get
public <T> T get(Class<T> entityClass, Serializable id) throws HibernateException {
return this.byId( entityClass ).load( id );
}
- SessionImpl#byId
public <T> IdentifierLoadAccessImpl<T> byId(Class<T> entityClass) {
return new IdentifierLoadAccessImpl<T>( entityClass );
}
- IdentifierLoadAccessImpl类
private class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
private final EntityPersister entityPersister;// 映相当于实体映射配置文件
private LockOptions lockOptions;
private CacheMode cacheMode;
}
IdentifierLoadAccessImpl生成一个loadEvent并附带特定的LoadType去触发监听器
- IdentifierLoadAccessImpl#getReference(SessionImpl#load路线)
public final T getReference(Serializable id) {
CacheMode sessionCacheMode = getCacheMode();
boolean cacheModeChanged = false;
if ( cacheMode != null ) {
// naive check for now...
// todo : account for "conceptually equal"
if ( cacheMode != sessionCacheMode ) {
setCacheMode( cacheMode );//将session的cacheMode临时改为IdentifierLoadAccessImpl的
cacheModeChanged = true;
}
}
try {
return doGetReference( id );//重点
}
finally {
if ( cacheModeChanged ) {
// change it back
setCacheMode( sessionCacheMode );
}
}
}
- IdentifierLoadAccessImpl#doGetReference
protected T doGetReference(Serializable id) {
if ( this.lockOptions != null ) {
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, SessionImpl.this );//生成一个LoadEvent
fireLoad( event, LoadEventListener.LOAD );//以LOAD类型触发监听器
return (T) event.getResult();
}
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, SessionImpl.this );//生成一个LoadEvent
boolean success = false;
try {
fireLoad( event, LoadEventListener.LOAD );//以LOAD类型触发监听器
if ( event.getResult() == null ) {
getFactory().getEntityNotFoundDelegate().handleEntityNotFound(
entityPersister.getEntityName(),
id
);
}
success = true;
return (T) event.getResult();
}
finally {
afterOperation( success );
}
}
Session.get这边也大同小异,区别在LoadType为GET类型
- IdentifierLoadAccessImpl#load(SessionImpl#get路线)
public final T load(Serializable id) {
CacheMode sessionCacheMode = getCacheMode();
boolean cacheModeChanged = false;
if ( cacheMode != null ) {
// naive check for now...
// todo : account for "conceptually equal"
if ( cacheMode != sessionCacheMode ) {
setCacheMode( cacheMode );
cacheModeChanged = true;
}
}
try {
return doLoad( id );
}
finally {
if ( cacheModeChanged ) {
// change it back
setCacheMode( sessionCacheMode );
}
}
}
- IdentifierLoadAccessImpl#doLoad
protected final T doLoad(Serializable id) {
if ( this.lockOptions != null ) {
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, SessionImpl.this );
fireLoad( event, LoadEventListener.GET );//GET类型
return (T) event.getResult();
}
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, SessionImpl.this );
boolean success = false;
try {
fireLoad( event, LoadEventListener.GET );//GET类型
success = true;
}
catch (ObjectNotFoundException e) {
// if session cache contains proxy for non-existing object
}
finally {
afterOperation( success );
}
return (T) event.getResult();
}
2.通知Listener
最后Session.load和Session.get都会汇入fireLoad方法中
- SessionImpl.fireLoad(LoadEvent event, LoadType loadType)
private void fireLoad(LoadEvent event, LoadType loadType) {
checkOpenOrWaitingForAutoClose();
checkTransactionSynchStatus();
for ( LoadEventListener listener : listeners( EventType.LOAD ) ) {
listener.onLoad( event, loadType );
}
delayedAfterCompletion();
}
EventType.LOAD表示这次是事件是查询事件,还有很多种EventType,比如SAVE、UPDATE、FLUSH等。
3.LoadListenr根据LoadType进行查找和返回结果
- DefaultLoadEventListener#onLoad
做一些检查,重点是doOnLoad方法
public void onLoad(
final LoadEvent event,
final LoadEventListener.LoadType loadType) throws HibernateException {
//根据event里的EntityName,从工厂获取持久器
final EntityPersister persister = getPersister( event );
if ( persister == null ) {
throw new HibernateException( "Unable to locate persister: " + event.getEntityClassName() );
}
final Class idClass = persister.getIdentifierType().getReturnedClass();
if ( idClass != null && !idClass.isInstance( event.getEntityId() ) ) {
checkIdClass( persister, event, loadType, idClass );
}
doOnLoad( persister, event, loadType );
}
- DefaultLoadEventListener#doOnLoad
从这里开始,LoadType将发挥作用
private void doOnLoad(
final EntityPersister persister,
final LoadEvent event,
final LoadEventListener.LoadType loadType) {
try {
final EntityKey keyToLoad = event.getSession().generateEntityKey( event.getEntityId(), persister );
if ( loadType.isNakedEntityReturned() ) {
//do not return a proxy!
//(this option indicates we are initializing a proxy)
event.setResult( load( event, persister, keyToLoad, loadType ) );
}
else { //LOAD和GET的isNakedEntityReturned均为false,走这个else分支
//return a proxy if appropriate
if ( event.getLockMode() == LockMode.NONE ) {
event.setResult( proxyOrLoad( event, persister, keyToLoad, loadType ) );
}
else {
event.setResult( lockAndLoad( event, persister, keyToLoad, loadType, event.getSession() ) );
}
}
}
catch (HibernateException e) {
LOG.unableToLoadCommand( e );
throw e;
}
}
依次分析load、proxyOrLoad、lockAndLoad三种情况
load
load不创建代理,它将尝试去查找实体
private Object load(
final LoadEvent event,
final EntityPersister persister,
final EntityKey keyToLoad,
final LoadEventListener.LoadType options) {
if ( event.getInstanceToLoad() != null ) {
if ( event.getSession().getPersistenceContext().getEntry( event.getInstanceToLoad() ) != null ) {
throw new PersistentObjectException(
"attempted to load into an instance that was already associated with the session: " +
MessageHelper.infoString(
persister,
event.getEntityId(),
event.getSession().getFactory()
)
);
}
persister.setIdentifier( event.getInstanceToLoad(), event.getEntityId(), event.getSession() );
}
final Object entity = doLoad( event, persister, keyToLoad, options );
boolean isOptionalInstance = event.getInstanceToLoad() != null;
//找不到实体且LoadType不允许空值时,要进行空值处理(GET允许空,LOAD不允许)
if ( entity == null && ( !options.isAllowNulls() || isOptionalInstance ) ) {
event.getSession()
.getFactory()
.getEntityNotFoundDelegate()
.handleEntityNotFound( event.getEntityClassName(), event.getEntityId() );
}
else if ( isOptionalInstance && entity != event.getInstanceToLoad() ) {
throw new NonUniqueObjectException( event.getEntityId(), event.getEntityClassName() );
}
return entity;
}
- DefaultLoadEventListener#doLoad
真正查找实体的地方,依次在第一缓存(Session)、第二缓存和数据库中查找。
private Object doLoad(
final LoadEvent event,
final EntityPersister persister,
final EntityKey keyToLoad,
final LoadEventListener.LoadType options) {
if ( traceEnabled ) {
LOG.tracev(
"Attempting to resolve: {0}",
MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
);
}
//在第一缓存查找
Object entity = loadFromSessionCache( event, keyToLoad, options );
if ( entity == REMOVED_ENTITY_MARKER ) {
LOG.debug( "Load request found matching entity in context, but it is scheduled for removal; returning null" );
return null;
}
if ( entity == INCONSISTENT_RTN_CLASS_MARKER ) {
LOG.debug(
"Load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null"
);
return null;
}
if ( entity != null ) {
if ( traceEnabled ) {
LOG.tracev(
"Resolved object in session cache: {0}",
MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
);
}
return entity;
}
//在第二缓存查找
entity = loadFromSecondLevelCache( event, persister, keyToLoad );
if ( entity != null ) {
if ( traceEnabled ) {
LOG.tracev(
"Resolved object in second-level cache: {0}",
MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
);
}
}
else {
if ( traceEnabled ) {
LOG.tracev(
"Object not resolved in any cache: {0}",
MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
);
}
//从数据库中查找
entity = loadFromDatasource( event, persister );
}
if ( entity != null && persister.hasNaturalIdentifier() ) {
event.getSession().getPersistenceContext().getNaturalIdHelper().cacheNaturalIdCrossReferenceFromLoad(
persister,
event.getEntityId(),
event.getSession().getPersistenceContext().getNaturalIdHelper().extractNaturalIdValues(
entity,
persister
)
);
}
return entity;
}
这里留意一下查找Session缓存的细节。Session的缓存是其persistenceContext成员,我们后面会再次遇到persistenceContext这个东西。
protected Object loadFromSessionCache(
// 其他代码
Object old = session.getEntityUsingInterceptor( keyToLoad );
// 其他代码
}
public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
// 其他代码
final Object result = persistenceContext.getEntity( key );
// 其他代码
}
proxyOrLoad
可能返回一个已经存在的代理、新代理或者实体。GET和LOAD走这里。
private Object proxyOrLoad(
final LoadEvent event,
final EntityPersister persister,
final EntityKey keyToLoad,
final LoadEventListener.LoadType options) {
if ( traceEnabled ) {
LOG.tracev(
"Loading entity: {0}",
MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
);
}
// this class has no proxies (so do a shortcut)
// 分支1:如果这个类的配置文件中没有设置代理,直接从一二级缓存和数据库查实体
if ( !persister.hasProxy() ) {
return load( event, persister, keyToLoad, options );
}
final PersistenceContext persistenceContext = event.getSession().getPersistenceContext();
// look for a proxy
// 分支2:一级缓存已经存在这个代理
Object proxy = persistenceContext.getProxy( keyToLoad );
if ( proxy != null ) {
return returnNarrowedProxy( event, persister, keyToLoad, options, persistenceContext, proxy );
}
// 分支3:如果允许创建代理,先看缓存中有没有实体或者已经初始化的代理,如果没有就新建代理。第一次LOAD时走这里
if ( options.isAllowProxyCreation() ) {
return createProxyIfNecessary( event, persister, keyToLoad, options, persistenceContext );
}
// return a newly loaded object
// 分支4:查实体。第一次GET的时候,走这里
return load( event, persister, keyToLoad, options );
}
分支1、4的查实体前面已经讲过
- 分支3 比较直观,先看它
private Object createProxyIfNecessary(
final LoadEvent event,
final EntityPersister persister,
final EntityKey keyToLoad,
final LoadEventListener.LoadType options,
final PersistenceContext persistenceContext) {
Object existing = persistenceContext.getEntity( keyToLoad );//从Session缓存取实体
if ( existing != null ) {
// return existing object or initialized proxy (unless deleted)
// 如果已经缓存了实体,就返回这个实体
if ( traceEnabled ) {
LOG.trace( "Entity found in session cache" );
}
if ( options.isCheckDeleted() ) {
EntityEntry entry = persistenceContext.getEntry( existing );
Status status = entry.getStatus();
if ( status == Status.DELETED || status == Status.GONE ) {
return null;
}
}
return existing;
}
if ( traceEnabled ) {
LOG.trace( "Creating new proxy for entity" );
}
// return new uninitialized proxy
// 返回一个未初始化的代理
Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
persistenceContext.addProxy( keyToLoad, proxy );
return proxy;
}
- 分支2
private Object returnNarrowedProxy(
final LoadEvent event,
final EntityPersister persister,
final EntityKey keyToLoad,
final LoadEventListener.LoadType options,
final PersistenceContext persistenceContext,
final Object proxy) {
if ( traceEnabled ) {
LOG.trace( "Entity proxy found in session cache" );
}
LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
if ( li.isUnwrap() ) {
return li.getImplementation();//代理初始化并返回其中的实体
}
Object impl = null;
if ( !options.isAllowProxyCreation() ) { //GET进入这个if,而LOAD不会
impl = load( event, persister, keyToLoad, options );
if ( impl == null ) {
event.getSession()
.getFactory()
.getEntityNotFoundDelegate()
.handleEntityNotFound( persister.getEntityName(), keyToLoad.getIdentifier() );
}
}
return persistenceContext.narrowProxy( proxy, persister, keyToLoad, impl );
}
- StatefulPersistenceContext#narrowProxy
尝试将proxy的类型“收窄“
为什么要做这个narrow的步骤?:
1.调用Sesion.load(Pet.class,catId)后,Session缓存的proxy将继承Pet类
2.继续Session.load(Cat.class,catId),Session将上一步继承Pet类的proxy直接返回是不妥的,应该将proxy继承的类”收窄“
关于narrow proxy的概念可以参考https://marcin-chwedczuk.github.io/HHH000179-narrowing-proxy-to-class-this-operation-breaks-equality
public Object narrowProxy(Object proxy, EntityPersister persister, EntityKey key, Object object)
throws HibernateException {
final Class concreteProxyClass = persister.getConcreteProxyClass();
final boolean alreadyNarrow = concreteProxyClass.isInstance( proxy );//已有的proxy继承的类和本次要查的类是否足够“具体”(比如曾经用Pet.class来创建过这个proxy,而现在要用Cat.class查询)
if ( !alreadyNarrow ) {
LOG.narrowingProxy( concreteProxyClass );
// If an impl is passed, there is really no point in creating a proxy.
// It would just be extra processing. Just return the impl
// 旧代理的类型“不够窄”,且已经给出了新的实现,那就没必要新建一个代理了
if ( object != null ) {
proxiesByKey.remove( key );
return object;
}
// Similarly, if the original HibernateProxy is initialized, there
// is again no point in creating a proxy. Just return the impl
// 同样的,旧代理已经初始化了,就没有必要新建一个代理,只取里面的实例即可
final HibernateProxy originalHibernateProxy = (HibernateProxy) proxy;
if ( !originalHibernateProxy.getHibernateLazyInitializer().isUninitialized() ) {
final Object impl = originalHibernateProxy.getHibernateLazyInitializer().getImplementation();
// can we return it?
if ( concreteProxyClass.isInstance( impl ) ) {
proxiesByKey.remove( key );
return impl;
}
}
// Otherwise, create the narrowed proxy
// 新建一个未初始化的,更“窄”的代理
final HibernateProxy narrowedProxy = (HibernateProxy) persister.createProxy( key.getIdentifier(), session );
// set the read-only/modifiable mode in the new proxy to what it was in the original proxy
final boolean readOnlyOrig = originalHibernateProxy.getHibernateLazyInitializer().isReadOnly();
narrowedProxy.getHibernateLazyInitializer().setReadOnly( readOnlyOrig );
return narrowedProxy;
}
else {
if ( object != null ) {
final LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
li.setImplementation( object );//将参数中的实体注入到proxy中,即手工初始化这个代理
}
return proxy;
}
}
narrowProxy接口的注释
/** * If the existing proxy is insufficiently "narrow" (derived), instantiate a new proxy * and overwrite the registration of the old one. This breaks == and occurs only for * "class" proxies rather than "interface" proxies. Also init the proxy to point to * the given target implementation if necessary. */
lockAndLoad
private Object lockAndLoad(
final LoadEvent event,
final EntityPersister persister,
final EntityKey keyToLoad,
final LoadEventListener.LoadType options,
final SessionImplementor source) {
SoftLock lock = null;
final Object ck;
final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
if ( persister.hasCache() ) {
ck = cache.generateCacheKey(
event.getEntityId(),
persister,
source.getFactory(),
source.getTenantIdentifier()
);
lock = persister.getCacheAccessStrategy().lockItem( source, ck, null );
}
else {
ck = null;
}
Object entity;
try {
entity = load( event, persister, keyToLoad, options );
}
finally {
if ( persister.hasCache() ) {
cache.unlockItem( source, ck, lock );
}
}
return event.getSession().getPersistenceContext().proxyFor( persister, keyToLoad, entity );
}
public Object proxyFor(EntityPersister persister, EntityKey key, Object impl) throws HibernateException {
if ( !persister.hasProxy() ) {
return impl;
}
final Object proxy = proxiesByKey.get( key );
return ( proxy != null ) ? narrowProxy( proxy, persister, key, impl ) : impl;
}
proxyFor接口的注释是这样写的
/** * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the * third argument (the entity associated with the key) if no proxy exists. Init * the proxy to the target implementation, if necessary. */
结论
Session里没有实体的proxy缓存
- LOAD:查Session有无实体,有就返回,没有就建个proxy
- GET:依次查询Session、二级缓存、数据库的实体
Session里有实体的proxy缓存
- LOAD
- 如果proxy类型不够“窄”,就看proxy状态:
proxy已初始化:取出里面的实体,并移除proxy缓存
proxy未初始化:新建窄proxy并返回- 如果proxy类型够“窄”:直接返回proxy
- GET:先依次查询Session、二级缓存、数据库的实体是否存在
- 如果proxy类型够“窄”:
- GET实体存在:将实体注入到proxy中,返回这个proxy
- GET实体不存在:返回这个proxy
- 如果proxy类型不够“窄”
- GET实体存在:返回这个实体
- GET实体不存在,看proxy状态:
proxy已初始化:取出里面的实体,并移除proxy缓存
proxy未初始化:新建窄proxy并返回