📄 loader.java
字号:
//$Id: Loader.java,v 1.97 2005/04/10 18:43:48 oneovthafew Exp $package org.hibernate.loader;import java.io.Serializable;import java.sql.CallableStatement;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.ArrayList;import java.util.Arrays;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.hibernate.AssertionFailure;import org.hibernate.Hibernate;import org.hibernate.HibernateException;import org.hibernate.LockMode;import org.hibernate.QueryException;import org.hibernate.ScrollMode;import org.hibernate.ScrollableResults;import org.hibernate.StaleObjectStateException;import org.hibernate.WrongClassException;import org.hibernate.cache.QueryCache;import org.hibernate.cache.QueryKey;import org.hibernate.collection.PersistentCollection;import org.hibernate.dialect.Dialect;import org.hibernate.engine.EntityKey;import org.hibernate.engine.EntityUniqueKey;import org.hibernate.engine.PersistenceContext;import org.hibernate.engine.QueryParameters;import org.hibernate.engine.RowSelection;import org.hibernate.engine.SessionFactoryImplementor;import org.hibernate.engine.SessionImplementor;import org.hibernate.engine.SubselectFetch;import org.hibernate.engine.TwoPhaseLoad;import org.hibernate.event.PostLoadEvent;import org.hibernate.event.PreLoadEvent;import org.hibernate.exception.JDBCExceptionHelper;import org.hibernate.impl.ScrollableResultsImpl;import org.hibernate.jdbc.ColumnNameCache;import org.hibernate.jdbc.ResultSetWrapper;import org.hibernate.persister.collection.CollectionPersister;import org.hibernate.persister.entity.EntityPersister;import org.hibernate.persister.entity.Loadable;import org.hibernate.persister.entity.UniqueKeyLoadable;import org.hibernate.pretty.MessageHelper;import org.hibernate.type.AssociationType;import org.hibernate.type.Type;import org.hibernate.type.VersionType;import org.hibernate.util.StringHelper;/** * Abstract superclass of object loading (and querying) strategies. This class implements * useful common functionality that concrete loaders delegate to. It is not intended that this * functionality would be directly accessed by client code. (Hence, all methods of this class * are declared <tt>protected</tt> or <tt>private</tt>.) This class relies heavily upon the * <tt>Loadable</tt> interface, which is the contract between this class and * <tt>EntityPersister</tt>s that may be loaded by it.<br> * <br> * The present implementation is able to load any number of columns of entities and at most * one collection role per query. * * @author Gavin King * @see org.hibernate.persister.entity.Loadable */public abstract class Loader { private static final Log log = LogFactory.getLog( Loader.class ); private final SessionFactoryImplementor factory; private ColumnNameCache columnNameCache; public Loader(SessionFactoryImplementor factory) { this.factory = factory; } /** * The SQL query string to be called; implemented by all subclasses */ protected abstract String getSQLString(); /** * An array of persisters of entity classes contained in each row of results; * implemented by all subclasses */ protected abstract Loadable[] getEntityPersisters(); /** * An array indicating whether the entities have eager property fetching * enabled */ protected boolean[] getEntityEagerPropertyFetches() { return null; } /** * An array of indexes of the entity that owns a one-to-one association * to the entity at the given index (-1 if there is no "owner") */ protected int[] getOwners() { return null; } /** * An array of unique key property names by which the corresponding * entities are referenced by other entities in the result set */ protected AssociationType[] getOwnerAssociationTypes() { return null; } /** * An (optional) persister for a collection to be initialized; only collection loaders * return a non-null value */ protected CollectionPersister getCollectionPersister() { return null; } /** * Get the index of the entity that owns the collection, or -1 * if there is no owner in the query results (ie. in the case of a * collection initializer) or no collection. */ protected int getCollectionOwner() { return -1; } /** * What lock mode does this load entities with? * * @param lockModes a collection of lock modes specified dynamically via the Query interface */ protected abstract LockMode[] getLockModes(Map lockModes); /** * Append <tt>FOR UPDATE OF</tt> clause, if necessary. This * empty superclass implementation merely returns its first * argument. */ protected String applyLocks(String sql, Map lockModes, Dialect dialect) throws HibernateException { return sql; } /** * Does this query return objects that might be already cached * by the session, whose lock mode may need upgrading */ protected boolean upgradeLocks() { return false; } /** * Return false is this loader is a batch entity loader */ protected boolean isSingleRowLoader() { return false; } /** * Get the SQL table aliases of entities whose * associations are subselect-loadable, returning * null if this loader does not support subselect * loading */ protected String[] getAliases() { return null; } /** * Modify the SQL, adding lock hints and comments, if necessary */ protected String preprocessSQL(String sql, QueryParameters parameters, Dialect dialect) throws HibernateException { sql = applyLocks( sql, parameters.getLockModes(), dialect ); String comment = parameters.getComment(); if ( comment == null ) { return sql; } else { return new StringBuffer( comment.length() + sql.length() + 5 ) .append( "/*" ) .append( comment ) .append( "*/ " ) .append( sql ) .toString(); } } /** * Execute an SQL query and attempt to instantiate instances of the class mapped by the given * persister from each row of the <tt>ResultSet</tt>. If an object is supplied, will attempt to * initialize that object. If a collection is supplied, attempt to initialize that collection. */ private List doQueryAndInitializeNonLazyCollections(final SessionImplementor session, final QueryParameters queryParameters, final boolean returnProxies) throws HibernateException, SQLException { final PersistenceContext persistenceContext = session.getPersistenceContext(); persistenceContext.beforeLoad(); List result; try { result = doQuery( session, queryParameters, returnProxies ); } finally { persistenceContext.afterLoad(); } persistenceContext.initializeNonLazyCollections(); return result; } public Object loadSingleRow(final ResultSet resultSet, final SessionImplementor session, final QueryParameters queryParameters, final boolean returnProxies) throws HibernateException { final int entitySpan = getEntityPersisters().length; final List hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan ); final Object result; try { result = getRowFromResultSet( resultSet, session, queryParameters, getLockModes( queryParameters.getLockModes() ), null, hydratedObjects, new EntityKey[entitySpan], returnProxies ); } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( factory.getSQLExceptionConverter(), sqle, "could not read next row of results", getSQLString() ); } initializeEntitiesAndCollections( hydratedObjects, resultSet, session, queryParameters.isReadOnly() ); session.getPersistenceContext().initializeNonLazyCollections(); return result; } private static EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) { final Object optionalObject = queryParameters.getOptionalObject(); final Serializable optionalId = queryParameters.getOptionalId(); final String optionalEntityName = queryParameters.getOptionalEntityName(); if ( optionalObject != null && optionalEntityName != null ) { return new EntityKey( optionalId, session.getEntityPersister( optionalEntityName, optionalObject ), session.getEntityMode() ); } else { return null; } } private Object getRowFromResultSet(final ResultSet resultSet, final SessionImplementor session, final QueryParameters queryParameters, final LockMode[] lockModeArray, final EntityKey optionalObjectKey, final List hydratedObjects, final EntityKey[] keys, boolean returnProxies) throws SQLException, HibernateException { final Loadable[] persisters = getEntityPersisters(); final int entitySpan = persisters.length; for ( int i = 0; i < entitySpan; i++ ) { keys[i] = getKeyFromResultSet( i, persisters[i], i == entitySpan - 1 ? queryParameters.getOptionalId() : null, resultSet, session ); //TODO: the i==entitySpan-1 bit depends upon subclass implementation (very bad) } registerNonExists( keys, persisters, session ); // this call is side-effecty Object[] row = getRow( resultSet, persisters, keys, queryParameters.getOptionalObject(), optionalObjectKey, lockModeArray, hydratedObjects, session ); readCollectionElements( row, resultSet, session ); if ( returnProxies ) { // now get an existing proxy for each row element (if there is one) for ( int i = 0; i < entitySpan; i++ ) { row[i] = session.getPersistenceContext().proxyFor( persisters[i], keys[i], row[i] ); // force the proxy to resolve itself Hibernate.initialize( row[i] ); //TODO: if there is an uninitialized proxy, this involves a hashmap lookup that could be avoided } } return getResultColumnOrRow( row, resultSet, session ); } /** * Read any collection elements contained in a single row of the result set */ private void readCollectionElements(Object[] row, ResultSet resultSet, SessionImplementor session) throws SQLException, HibernateException { //TODO: make this handle multiple collection roles! final CollectionPersister collectionPersister = getCollectionPersister(); if ( collectionPersister != null ) { final int collectionOwner = getCollectionOwner(); final boolean hasCollectionOwners = collectionOwner >= 0; //true if this is a query and we are loading multiple instances of the same collection role //otherwise this is a CollectionInitializer and we are loading up a single collection or batch final Object owner = hasCollectionOwners ? row[collectionOwner] : null; //if null, owner will be retrieved from session final Serializable key; if ( owner == null ) { key = null; } else { key = collectionPersister.getCollectionType().getKeyOfOwner( owner, session ); //TODO: old version did not require hashmap lookup: //keys[collectionOwner].getIdentifier() } readCollectionElement( owner, key, resultSet, session ); } } 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 List results = new ArrayList(); final PreparedStatement st = prepareQueryStatement( queryParameters, false, session ); final ResultSet rs = getResultSet( st, queryParameters.isCallable(), selection, session ); final LockMode[] lockModeArray = getLockModes( queryParameters.getLockModes() ); final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session ); final boolean createSubselects = isSubselectLoadingEnabled(); final List subselectResultKeys = createSubselects ? new ArrayList() : null; 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, lockModeArray, 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)" );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -