发现hibernate的代码还是挺复杂的,比spring ,struts要复杂的多,所以就一段段看。
今天主要分析一下hibernate如何来更新数据做CURP操作。
先看看load 和 get
public Object load(String entityName, Serializable id) throws HibernateException { LoadEvent event = new LoadEvent(id, entityName, false, this); boolean success = false; try { fireLoad( event, LoadEventListener.LOAD ); if ( event.getResult() == null ) { getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id ); } success = true; return event.getResult(); } finally { afterOperation(success); } }
get:
public Object get(String entityName, Serializable id) throws HibernateException { LoadEvent event = new LoadEvent(id, entityName, false, this); boolean success = false; try { fireLoad(event, LoadEventListener.GET); success = true; return event.getResult(); } finally { afterOperation(success); } }
对比这两个的代码发现只有
if ( event.getResult() == null ) { getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id ); }
差别,其余都几乎一样,这行也没起多大作用, 仔细看发现调用fireLoad时的第2个参数不一样一个是LoadEventListener.GET, LoadEventListener.LOAD
跟踪
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);
这里看名字就可以看出get允许为null, load不允许, 另外setAllowProxyCreation 值不一样,其他都一样。
下面看下fireLoad方法:
private void fireLoad(LoadEvent event, LoadType loadType) { errorIfClosed(); checkTransactionSynchStatus(); LoadEventListener[] loadEventListener = listeners.getLoadEventListeners(); for ( int i = 0; i < loadEventListener.length; i++ ) { loadEventListener[i].onLoad(event, loadType); } }
这里是对LoadEvent进行处理,调用已经注册好的LoadEventListener 去处理这个onLoad事件
这里的LoadEventListener的实现类是DefaultLoadEventListener
要弄明白hibernate是怎么获得一个数据的,就要看onLoad 方法了
/** * Handle the given load event. * * @param event The load event to be handled. * @throws HibernateException */ public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType) throws HibernateException { final SessionImplementor source = event.getSession(); EntityPersister persister; //获得当前pojo 的 EntityPersister if ( event.getInstanceToLoad() != null ) { persister = source.getEntityPersister( null, event.getInstanceToLoad() ); //the load() which takes an entity does not pass an entityName event.setEntityClassName( event.getInstanceToLoad().getClass().getName() ); } else { persister = source.getFactory().getEntityPersister( event.getEntityClassName() ); } if ( persister == null ) { throw new HibernateException( "Unable to locate persister: " + event.getEntityClassName() ); } final Class idClass = persister.getIdentifierType().getReturnedClass(); if ( persister.getIdentifierType().isComponentType() && EntityMode.DOM4J == event.getSession().getEntityMode() ) { // skip this check for composite-ids relating to dom4j entity-mode; // alternatively, we could add a check to make sure the incoming id value is // an instance of Element... } else { if ( idClass != null && ! idClass.isInstance( event.getEntityId() ) ) { // we may have the kooky jpa requirement of allowing find-by-id where // "id" is the "simple pk value" of a dependent objects parent. This // is part of its generally goofy "derived identity" "feature" if ( persister.getEntityMetamodel().getIdentifierProperty().isEmbedded() ) { final EmbeddedComponentType dependentIdType = (EmbeddedComponentType) persister.getEntityMetamodel().getIdentifierProperty().getType(); if ( dependentIdType.getSubtypes().length == 1 ) { final Type singleSubType = dependentIdType.getSubtypes()[0]; if ( singleSubType.isEntityType() ) { final EntityType dependentParentType = (EntityType) singleSubType; final Type dependentParentIdType = dependentParentType.getIdentifierOrUniqueKeyType( source.getFactory() ); if ( dependentParentIdType.getReturnedClass().isInstance( event.getEntityId() ) ) { // yep that's what we have... loadByDerivedIdentitySimplePkValue( event, loadType, persister, dependentIdType, source.getFactory().getEntityPersister( dependentParentType.getAssociatedEntityName() ) ); return; } } } } throw new TypeMismatchException( "Provided id of the wrong type for class " + persister.getEntityName() + ". Expected: " + idClass + ", got " + event.getEntityId().getClass() ); } } EntityKey keyToLoad = new EntityKey( event.getEntityId(), persister, source.getEntityMode() ); try { if ( loadType.isNakedEntityReturned() ) { //do not return a proxy! //(this option indicates we are initializing a proxy) event.setResult( load(event, persister, keyToLoad, loadType) ); } 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, source) ); } } } catch(HibernateException e) { log.info("Error performing load command", e); throw e; } }
这里最后调用event.setResult( proxyOrLoad(event, persister, keyToLoad, loadType) ); 在默认无锁的情况下。
protected Object proxyOrLoad( final LoadEvent event, final EntityPersister persister, final EntityKey keyToLoad, final LoadEventListener.LoadType options) { if ( log.isTraceEnabled() ) { log.trace( "loading entity: " + MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() ) ); } if ( !persister.hasProxy() ) { // this class has no proxies (so do a shortcut) return load(event, persister, keyToLoad, options); } else { final PersistenceContext persistenceContext = event.getSession().getPersistenceContext(); // look for a proxy Object proxy = persistenceContext.getProxy(keyToLoad); if ( proxy != null ) { return returnNarrowedProxy( event, persister, keyToLoad, options, persistenceContext, proxy ); } else { if ( options.isAllowProxyCreation() ) { return createProxyIfNecessary( event, persister, keyToLoad, options, persistenceContext ); } else { // return a newly loaded object return load(event, persister, keyToLoad, options); } } } }
protected Object doLoad( final LoadEvent event, final EntityPersister persister, final EntityKey keyToLoad, final LoadEventListener.LoadType options) { if ( log.isTraceEnabled() ) { log.trace( "attempting to resolve: " + 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 ( log.isTraceEnabled() ) { log.trace( "resolved object in session cache: " + MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() ) ); } return entity; } entity = loadFromSecondLevelCache(event, persister, options); if ( entity != null ) { if ( log.isTraceEnabled() ) { log.trace( "resolved object in second-level cache: " + MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() ) ); } return entity; } if ( log.isTraceEnabled() ) { log.trace( "object not resolved in any cache: " + MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() ) ); } return loadFromDatasource(event, persister, keyToLoad, options); }
private Object createProxyIfNecessary( final LoadEvent event, final EntityPersister persister, final EntityKey keyToLoad, final LoadEventListener.LoadType options, final PersistenceContext persistenceContext) { Object existing = persistenceContext.getEntity( keyToLoad ); if ( existing != null ) { // return existing object or initialized proxy (unless deleted) 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; } else { 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; } }
hibernate 是采用事件回调模式去处理一个数据库的操作,通过load返回的实际上是一个proxy的对象, 在debug模式下查看这个proxy 对象得到
"proxy"= Event_$$_javassist_0 (id=78)
看来这个好像是用javassist类库来增强过的pojo对象,load返回的就是这个对象了。
如果是get方法将会调用 doLoad 方法去获得
if ( options.isAllowProxyCreation() ) { return createProxyIfNecessary( event, persister, keyToLoad, options, persistenceContext ); } else { // return a newly loaded object return load(event, persister, keyToLoad, options); }
get 方式的时候 isAllowProxyCreation 值为false的,参看前面的代码
doLoad方法先去sessionCache然后再去secondCache去查找 给定对象,如果没有的话就调用
return loadFromDatasource(event, persister, keyToLoad, options);
从数据库找,代码:
/** * Performs the process of loading an entity from the configured * underlying datasource. * * @param event The load event * @param persister The persister for the entity being requested for load * @param keyToLoad The EntityKey representing the entity to be loaded. * @param options The load options. * @return The object loaded from the datasource, or null if not found. */ protected Object loadFromDatasource( final LoadEvent event, final EntityPersister persister, final EntityKey keyToLoad, final LoadEventListener.LoadType options) { final SessionImplementor source = event.getSession(); Object entity = persister.load( event.getEntityId(), event.getInstanceToLoad(), event.getLockOptions(), source ); if ( event.isAssociationFetch() && source.getFactory().getStatistics().isStatisticsEnabled() ) { source.getFactory().getStatisticsImplementor().fetchEntity( event.getEntityClassName() ); } return entity; }
这里最终调用 AbstractEntityPersister.load 方法,代码:
public Object load(Serializable id, Object optionalObject, LockOptions lockOptions, SessionImplementor session) throws HibernateException { if ( log.isTraceEnabled() ) { log.trace( "Fetching entity: " + MessageHelper.infoString( this, id, getFactory() ) ); } final UniqueEntityLoader loader = getAppropriateLoader(lockOptions, session ); return loader.load( id, optionalObject, session, lockOptions ); }
这里会得到一个合适的loader, 我测试的时候得到的是 org.hibernate.loader.entity.EntityLoader
它继承与 AbstractEntityLoader
public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) { return load( session, id, optionalObject, id, lockOptions ); } protected Object load( SessionImplementor session, Object id, Object optionalObject, Serializable optionalId, LockOptions lockOptions) { List list = loadEntity( session, id, uniqueKeyType, optionalObject, entityName, optionalId, persister, lockOptions ); if ( list.size()==1 ) { return list.get(0); } else if ( list.size()==0 ) { return null; } else { if ( getCollectionOwners()!=null ) { return list.get(0); } else { throw new HibernateException( "More than one row with the given identifier was found: " + id + ", for class: " + persister.getEntityName() ); } } }
调用 loadEntity 方法, 代码:
protected final List loadEntity( final SessionImplementor session, final Object id, final Type identifierType, final Object optionalObject, final String optionalEntityName, final Serializable optionalIdentifier, final EntityPersister persister, LockOptions lockOptions) throws HibernateException { if ( log.isDebugEnabled() ) { log.debug( "loading entity: " + MessageHelper.infoString( persister, id, identifierType, getFactory() ) ); } List result; try { QueryParameters qp = new QueryParameters(); qp.setPositionalParameterTypes( new Type[] { identifierType } ); qp.setPositionalParameterValues( new Object[] { id } ); qp.setOptionalObject( optionalObject ); qp.setOptionalEntityName( optionalEntityName ); qp.setOptionalId( optionalIdentifier ); qp.setLockOptions( lockOptions ); result = doQueryAndInitializeNonLazyCollections( session, qp, false ); } catch ( SQLException sqle ) { final Loadable[] persisters = getEntityPersisters(); throw JDBCExceptionHelper.convert( factory.getSQLExceptionConverter(), sqle, "could not load an entity: " + MessageHelper.infoString( persisters[persisters.length-1], id, identifierType, getFactory() ), getSQLString() ); } log.debug("done entity load"); return result; }
继续追踪,跟到方法
private List doQuery( final SessionImplementor session, final QueryParameters queryParameters, final boolean returnProxies) throws SQLException, HibernateException { final RowSelection selection = queryParameters.getRowSelection(); final int maxRows = hasMaxRows( selection ) ? selection.getMaxRows().intValue() : Integer.MAX_VALUE; final int entitySpan = getEntityPersisters().length; final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 ); final PreparedStatement st = prepareQueryStatement( queryParameters, false, session ); final ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), selection, session ); // would be great to move all this below here into another method that could also be used // from the new scrolling stuff. // // Would need to change the way the max-row stuff is handled (i.e. behind an interface) so // that I could do the control breaking at the means to know when to stop final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session ); final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() ); final boolean createSubselects = isSubselectLoadingEnabled(); final List subselectResultKeys = createSubselects ? new ArrayList() : null; final List results = new ArrayList(); try { handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session ); EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row if ( log.isTraceEnabled() ) log.trace( "processing result set" ); int count; for ( count = 0; count < maxRows && rs.next(); count++ ) { if ( log.isTraceEnabled() ) log.debug("result set row: " + count); Object result = getRowFromResultSet( rs, session, queryParameters, lockModesArray, optionalObjectKey, hydratedObjects, keys, returnProxies ); results.add( result ); if ( createSubselects ) { subselectResultKeys.add(keys); keys = new EntityKey[entitySpan]; //can't reuse in this case } } if ( log.isTraceEnabled() ) { log.trace( "done processing result set (" + count + " rows)" ); } } finally { session.getBatcher().closeQueryStatement( st, rs ); } initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly( session ) ); if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session ); return results; //getResultList(results); }
这里进行最终的数据库读取,