📄 loader.java
字号:
//$Id: Loader.java,v 1.33.2.29 2004/01/26 04:57:08 oneovthafew Exp $package net.sf.hibernate.loader;import java.io.Serializable;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.Set;import net.sf.hibernate.AssertionFailure;import net.sf.hibernate.HibernateException;import net.sf.hibernate.LockMode;import net.sf.hibernate.QueryException;import net.sf.hibernate.StaleObjectStateException;import net.sf.hibernate.WrongClassException;import net.sf.hibernate.cache.QueryCache;import net.sf.hibernate.cache.QueryKey;import net.sf.hibernate.cfg.Environment;import net.sf.hibernate.collection.CollectionPersister;import net.sf.hibernate.collection.PersistentCollection;import net.sf.hibernate.dialect.Dialect;import net.sf.hibernate.engine.Key;import net.sf.hibernate.engine.QueryParameters;import net.sf.hibernate.engine.RowSelection;import net.sf.hibernate.engine.SessionFactoryImplementor;import net.sf.hibernate.engine.SessionImplementor;import net.sf.hibernate.impl.MessageHelper;import net.sf.hibernate.persister.Loadable;import net.sf.hibernate.type.Type;import net.sf.hibernate.util.JDBCExceptionReporter;import net.sf.hibernate.util.StringHelper;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;/** * 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>ClassPersister</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. * * @see net.sf.hibernate.persister.Loadable * @author Gavin King */public abstract class Loader { private static final Log log = LogFactory.getLog(Loader.class); /** * 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[] getPersisters(); /** * The suffix identifies a particular column of results in the SQL <tt>ResultSet</tt>; * implemented by all subclasses */ protected abstract String[] getSuffixes(); /** * 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 abstract int[] getOwners(); /** * An (optional) persister for a collection to be initialized; only collection loaders * return a non-null value */ protected abstract CollectionPersister getCollectionPersister(); /** * 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; } /** * 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 Object optionalObject, final Serializable optionalId, final Serializable[] optionalCollectionKeys, final boolean returnProxies ) throws SQLException, HibernateException { session.beforeLoad(); List result; try { result = doQuery(session, queryParameters, optionalObject, optionalId, optionalCollectionKeys, returnProxies); } finally { session.afterLoad(); } session.initializeNonLazyCollections(); return result; } private List doQuery( final SessionImplementor session, final QueryParameters queryParameters, final Object optionalObject, final Serializable optionalId, final Serializable[] optionalCollectionKeys, boolean returnProxies ) throws SQLException, HibernateException { returnProxies = returnProxies && Environment.jvmSupportsProxies(); RowSelection selection = queryParameters.getRowSelection(); int maxRows = hasMaxRows(selection) ? selection.getMaxRows().intValue() : Integer.MAX_VALUE; final Loadable[] persisters = getPersisters(); final int cols = persisters.length; final CollectionPersister collectionPersister = getCollectionPersister(); final int collectionOwner = getCollectionOwner(); final boolean returnsEntities = cols > 0; final String[] suffixes = getSuffixes(); final LockMode[] lockModeArray = getLockModes( queryParameters.getLockModes() ); final int[] owners = getOwners(); //this is a CollectionInitializer and we are loading up a single collection final boolean hasCollections = collectionPersister!=null; //this is a query and we are loading multiple instances of the same collection role final boolean hasCollectionOwners = hasCollections && collectionOwner>=0; final ArrayList hydratedObjects = returnsEntities ? new ArrayList() : null; final Key optionalObjectKey; if (optionalObject!=null) { optionalObjectKey = new Key( optionalId, session.getPersister(optionalObject) ); } else { optionalObjectKey = null; } final List results = new ArrayList(); //new net.sf.hibernate.collections.List(this); final PreparedStatement st = prepareQueryStatement( applyLocks( getSQLString(), queryParameters.getLockModes(), session.getFactory().getDialect() ), queryParameters, false, session ); final ResultSet rs = getResultSet(st, selection, session); try { if (optionalCollectionKeys!=null) handleEmptyCollections(optionalCollectionKeys, rs, session); final Key[] keys = new Key[cols]; //we can reuse it each time if ( log.isTraceEnabled() ) log.trace("processing result set"); int count; for ( count=0; count<maxRows && rs.next(); count++ ) { for ( int i=0; i<cols; i++ ) { keys[i] = getKeyFromResultSet( i, persisters[i], (i==cols-1) ? optionalId : null, rs, session ); //TODO: the i==cols-1 bit depends upon subclass implementation (very bad) } if (owners!=null) registerNonExists(keys, owners, persisters, session); // this call is side-effecty Object[] row = getRow(rs, persisters, suffixes, keys, optionalObject, optionalObjectKey, lockModeArray, hydratedObjects, session); if (returnProxies) { // now get an existing proxy for each row element (if there is one) for ( int i=0; i<cols; i++ ) row[i] = session.proxyFor( persisters[i], keys[i], row[i] ); } if (hasCollections) { Object owner = hasCollectionOwners ? row[collectionOwner] : null; //if null, owner will be retrieved from session Serializable key = owner!=null ? keys[collectionOwner].getIdentifier() : null; readCollectionElement(owner, key, rs, session); } results.add( getResultColumnOrRow(row, rs, session) ); } if ( log.isTraceEnabled() ) log.trace("done processing result set (" + count + " rows)"); } catch (SQLException sqle) { JDBCExceptionReporter.logExceptions(sqle); throw sqle; } finally { session.getBatcher().closeQueryStatement(st, rs); } if (returnsEntities) { int hydratedObjectsSize = hydratedObjects.size(); if ( log.isTraceEnabled() ) log.trace("total objects hydrated: " + hydratedObjectsSize); for ( int i=0; i<hydratedObjectsSize; i++ ) session.initializeEntity( hydratedObjects.get(i) ); } if (hasCollections) session.endLoadingCollections(collectionPersister, rs); return results; //getResultList(results); } protected List getResultList(List results) throws QueryException { return results; } /** * Get the actual object that is returned in the user-visible result list. * This empty implementation merely returns its first argument. This is * overridden by some subclasses. */ protected Object getResultColumnOrRow(Object[] row, ResultSet rs, SessionImplementor session) throws SQLException, HibernateException { return row; } private void registerNonExists( final Key[] keys, final int[] owners, final Loadable[] persisters, final SessionImplementor session) { for (int i=0; i<keys.length; i++) { int owner = owners[i]; if (owner>-1) { Key ownerKey = keys[owner]; if ( keys[i]==null && ownerKey!=null ) { session.addNonExist( new Key( ownerKey.getIdentifier(), persisters[i] ) ); } } } } /** * Read one collection element from the current row of the JDBC result set */ private void readCollectionElement( final Object optionalOwner, final Serializable optionalKey, final ResultSet rs, final SessionImplementor session) throws HibernateException, SQLException { final CollectionPersister collectionPersister = getCollectionPersister(); final Serializable collectionRowKey = (Serializable) collectionPersister.readKey(rs, session); if (collectionRowKey!=null) { if ( log.isDebugEnabled() ) log.debug( "found row of collection: " + MessageHelper.infoString(collectionPersister, collectionRowKey) ); Object owner = optionalOwner; if (owner==null) { owner = session.getCollectionOwner(collectionRowKey, collectionPersister); if (owner==null) { //TODO: This is assertion is disabled because there is a bug that means the // original owner of a transient, uninitialized collection is not known // if the collection is re-referenced by a different object associated // with the current Session //throw new AssertionFailure("bug loading unowned collection"); } } PersistentCollection rowCollection = session.getLoadingCollection(collectionPersister, collectionRowKey, rs); if (rowCollection!=null) rowCollection.readFrom(rs, collectionPersister, owner); } else if (optionalKey!=null) { if ( log.isDebugEnabled() ) log.debug( "result set contains (possibly empty) collection: " + MessageHelper.infoString(collectionPersister, optionalKey) ); session.getLoadingCollection(collectionPersister, optionalKey, rs); //handle empty collection } } /** * If this is a collection initializer, we need to tell the session that a collection * is being initilized, to account for the possibility of the collection having * no elements (hence no rows in the result set). */ private void handleEmptyCollections( final Serializable[] keys, final Object resultSetId, final SessionImplementor session) throws HibernateException { CollectionPersister collectionPersister = getCollectionPersister(); for ( int i=0; i<keys.length; i++ ) { //handle empty collections if ( log.isDebugEnabled() ) log.debug( "result set contains (possibly empty) collection: " + MessageHelper.infoString(collectionPersister, keys[i]) ); session.getLoadingCollection(collectionPersister, keys[i], resultSetId); } } /** * Read a row of <tt>Key</tt>s from the <tt>ResultSet</tt> into the given array. * Warning: this method is side-effecty. * * If an <tt>id</tt> is given, don't bother going to the <tt>ResultSet</tt>. */ private Key getKeyFromResultSet(int i, Loadable persister, Serializable id, ResultSet rs, SessionImplementor session) throws HibernateException, SQLException { Serializable resultId;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -