Source Code Cross Referenced for Loader.java in  » Database-ORM » hibernate » org » hibernate » loader » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » hibernate » org.hibernate.loader 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        //$Id: Loader.java 11301 2007-03-19 20:43:46Z steve.ebersole@jboss.com $
0002:        package org.hibernate.loader;
0003:
0004:        import java.io.Serializable;
0005:        import java.sql.CallableStatement;
0006:        import java.sql.PreparedStatement;
0007:        import java.sql.ResultSet;
0008:        import java.sql.SQLException;
0009:        import java.util.ArrayList;
0010:        import java.util.Arrays;
0011:        import java.util.HashMap;
0012:        import java.util.HashSet;
0013:        import java.util.Iterator;
0014:        import java.util.List;
0015:        import java.util.Map;
0016:        import java.util.Set;
0017:
0018:        import org.apache.commons.logging.Log;
0019:        import org.apache.commons.logging.LogFactory;
0020:        import org.hibernate.AssertionFailure;
0021:        import org.hibernate.HibernateException;
0022:        import org.hibernate.LockMode;
0023:        import org.hibernate.QueryException;
0024:        import org.hibernate.ScrollMode;
0025:        import org.hibernate.ScrollableResults;
0026:        import org.hibernate.StaleObjectStateException;
0027:        import org.hibernate.WrongClassException;
0028:        import org.hibernate.cache.FilterKey;
0029:        import org.hibernate.cache.QueryCache;
0030:        import org.hibernate.cache.QueryKey;
0031:        import org.hibernate.collection.PersistentCollection;
0032:        import org.hibernate.dialect.Dialect;
0033:        import org.hibernate.engine.EntityKey;
0034:        import org.hibernate.engine.EntityUniqueKey;
0035:        import org.hibernate.engine.PersistenceContext;
0036:        import org.hibernate.engine.QueryParameters;
0037:        import org.hibernate.engine.RowSelection;
0038:        import org.hibernate.engine.SessionFactoryImplementor;
0039:        import org.hibernate.engine.SessionImplementor;
0040:        import org.hibernate.engine.SubselectFetch;
0041:        import org.hibernate.engine.TwoPhaseLoad;
0042:        import org.hibernate.engine.TypedValue;
0043:        import org.hibernate.event.EventSource;
0044:        import org.hibernate.event.PostLoadEvent;
0045:        import org.hibernate.event.PreLoadEvent;
0046:        import org.hibernate.exception.JDBCExceptionHelper;
0047:        import org.hibernate.hql.HolderInstantiator;
0048:        import org.hibernate.impl.FetchingScrollableResultsImpl;
0049:        import org.hibernate.impl.ScrollableResultsImpl;
0050:        import org.hibernate.jdbc.ColumnNameCache;
0051:        import org.hibernate.jdbc.ResultSetWrapper;
0052:        import org.hibernate.persister.collection.CollectionPersister;
0053:        import org.hibernate.persister.entity.EntityPersister;
0054:        import org.hibernate.persister.entity.Loadable;
0055:        import org.hibernate.persister.entity.UniqueKeyLoadable;
0056:        import org.hibernate.pretty.MessageHelper;
0057:        import org.hibernate.proxy.HibernateProxy;
0058:        import org.hibernate.transform.ResultTransformer;
0059:        import org.hibernate.type.AssociationType;
0060:        import org.hibernate.type.EntityType;
0061:        import org.hibernate.type.Type;
0062:        import org.hibernate.type.VersionType;
0063:        import org.hibernate.util.StringHelper;
0064:
0065:        /**
0066:         * Abstract superclass of object loading (and querying) strategies. This class implements
0067:         * useful common functionality that concrete loaders delegate to. It is not intended that this
0068:         * functionality would be directly accessed by client code. (Hence, all methods of this class
0069:         * are declared <tt>protected</tt> or <tt>private</tt>.) This class relies heavily upon the
0070:         * <tt>Loadable</tt> interface, which is the contract between this class and
0071:         * <tt>EntityPersister</tt>s that may be loaded by it.<br>
0072:         * <br>
0073:         * The present implementation is able to load any number of columns of entities and at most
0074:         * one collection role per query.
0075:         *
0076:         * @author Gavin King
0077:         * @see org.hibernate.persister.entity.Loadable
0078:         */
0079:        public abstract class Loader {
0080:
0081:            private static final Log log = LogFactory.getLog(Loader.class);
0082:
0083:            private final SessionFactoryImplementor factory;
0084:            private ColumnNameCache columnNameCache;
0085:
0086:            public Loader(SessionFactoryImplementor factory) {
0087:                this .factory = factory;
0088:            }
0089:
0090:            /**
0091:             * The SQL query string to be called; implemented by all subclasses
0092:             *
0093:             * @return The sql command this loader should use to get its {@link ResultSet}.
0094:             */
0095:            protected abstract String getSQLString();
0096:
0097:            /**
0098:             * An array of persisters of entity classes contained in each row of results;
0099:             * implemented by all subclasses
0100:             *
0101:             * @return The entity persisters.
0102:             */
0103:            protected abstract Loadable[] getEntityPersisters();
0104:
0105:            /**
0106:             * An array indicating whether the entities have eager property fetching
0107:             * enabled.
0108:             *
0109:             * @return Eager property fetching indicators.
0110:             */
0111:            protected boolean[] getEntityEagerPropertyFetches() {
0112:                return null;
0113:            }
0114:
0115:            /**
0116:             * An array of indexes of the entity that owns a one-to-one association
0117:             * to the entity at the given index (-1 if there is no "owner").  The
0118:             * indexes contained here are relative to the result of
0119:             * {@link #getEntityPersisters}.
0120:             *
0121:             * @return The owner indicators (see discussion above).
0122:             */
0123:            protected int[] getOwners() {
0124:                return null;
0125:            }
0126:
0127:            /**
0128:             * An array of the owner types corresponding to the {@link #getOwners()}
0129:             * returns.  Indices indicating no owner would be null here.
0130:             *
0131:             * @return The types for the owners.
0132:             */
0133:            protected EntityType[] getOwnerAssociationTypes() {
0134:                return null;
0135:            }
0136:
0137:            /**
0138:             * An (optional) persister for a collection to be initialized; only 
0139:             * collection loaders return a non-null value
0140:             */
0141:            protected CollectionPersister[] getCollectionPersisters() {
0142:                return null;
0143:            }
0144:
0145:            /**
0146:             * Get the index of the entity that owns the collection, or -1
0147:             * if there is no owner in the query results (ie. in the case of a
0148:             * collection initializer) or no collection.
0149:             */
0150:            protected int[] getCollectionOwners() {
0151:                return null;
0152:            }
0153:
0154:            /**
0155:             * What lock mode does this load entities with?
0156:             *
0157:             * @param lockModes a collection of lock modes specified dynamically via the Query interface
0158:             */
0159:            protected abstract LockMode[] getLockModes(Map lockModes);
0160:
0161:            /**
0162:             * Append <tt>FOR UPDATE OF</tt> clause, if necessary. This
0163:             * empty superclass implementation merely returns its first
0164:             * argument.
0165:             */
0166:            protected String applyLocks(String sql, Map lockModes,
0167:                    Dialect dialect) throws HibernateException {
0168:                return sql;
0169:            }
0170:
0171:            /**
0172:             * Does this query return objects that might be already cached
0173:             * by the session, whose lock mode may need upgrading
0174:             */
0175:            protected boolean upgradeLocks() {
0176:                return false;
0177:            }
0178:
0179:            /**
0180:             * Return false is this loader is a batch entity loader
0181:             */
0182:            protected boolean isSingleRowLoader() {
0183:                return false;
0184:            }
0185:
0186:            /**
0187:             * Get the SQL table aliases of entities whose
0188:             * associations are subselect-loadable, returning
0189:             * null if this loader does not support subselect
0190:             * loading
0191:             */
0192:            protected String[] getAliases() {
0193:                return null;
0194:            }
0195:
0196:            /**
0197:             * Modify the SQL, adding lock hints and comments, if necessary
0198:             */
0199:            protected String preprocessSQL(String sql,
0200:                    QueryParameters parameters, Dialect dialect)
0201:                    throws HibernateException {
0202:
0203:                sql = applyLocks(sql, parameters.getLockModes(), dialect);
0204:
0205:                return getFactory().getSettings().isCommentsEnabled() ? prependComment(
0206:                        sql, parameters)
0207:                        : sql;
0208:            }
0209:
0210:            private String prependComment(String sql, QueryParameters parameters) {
0211:                String comment = parameters.getComment();
0212:                if (comment == null) {
0213:                    return sql;
0214:                } else {
0215:                    return new StringBuffer(comment.length() + sql.length() + 5)
0216:                            .append("/* ").append(comment).append(" */ ")
0217:                            .append(sql).toString();
0218:                }
0219:            }
0220:
0221:            /**
0222:             * Execute an SQL query and attempt to instantiate instances of the class mapped by the given
0223:             * persister from each row of the <tt>ResultSet</tt>. If an object is supplied, will attempt to
0224:             * initialize that object. If a collection is supplied, attempt to initialize that collection.
0225:             */
0226:            private List doQueryAndInitializeNonLazyCollections(
0227:                    final SessionImplementor session,
0228:                    final QueryParameters queryParameters,
0229:                    final boolean returnProxies) throws HibernateException,
0230:                    SQLException {
0231:
0232:                final PersistenceContext persistenceContext = session
0233:                        .getPersistenceContext();
0234:                persistenceContext.beforeLoad();
0235:                List result;
0236:                try {
0237:                    result = doQuery(session, queryParameters, returnProxies);
0238:                } finally {
0239:                    persistenceContext.afterLoad();
0240:                }
0241:                persistenceContext.initializeNonLazyCollections();
0242:                return result;
0243:            }
0244:
0245:            /**
0246:             * Loads a single row from the result set.  This is the processing used from the
0247:             * ScrollableResults where no collection fetches were encountered.
0248:             *
0249:             * @param resultSet The result set from which to do the load.
0250:             * @param session The session from which the request originated.
0251:             * @param queryParameters The query parameters specified by the user.
0252:             * @param returnProxies Should proxies be generated
0253:             * @return The loaded "row".
0254:             * @throws HibernateException
0255:             */
0256:            public Object loadSingleRow(final ResultSet resultSet,
0257:                    final SessionImplementor session,
0258:                    final QueryParameters queryParameters,
0259:                    final boolean returnProxies) throws HibernateException {
0260:
0261:                final int entitySpan = getEntityPersisters().length;
0262:                final List hydratedObjects = entitySpan == 0 ? null
0263:                        : new ArrayList(entitySpan);
0264:
0265:                final Object result;
0266:                try {
0267:                    result = getRowFromResultSet(resultSet, session,
0268:                            queryParameters, getLockModes(queryParameters
0269:                                    .getLockModes()), null, hydratedObjects,
0270:                            new EntityKey[entitySpan], returnProxies);
0271:                } catch (SQLException sqle) {
0272:                    throw JDBCExceptionHelper.convert(factory
0273:                            .getSQLExceptionConverter(), sqle,
0274:                            "could not read next row of results",
0275:                            getSQLString());
0276:                }
0277:
0278:                initializeEntitiesAndCollections(hydratedObjects, resultSet,
0279:                        session, queryParameters.isReadOnly());
0280:                session.getPersistenceContext().initializeNonLazyCollections();
0281:                return result;
0282:            }
0283:
0284:            private Object sequentialLoad(final ResultSet resultSet,
0285:                    final SessionImplementor session,
0286:                    final QueryParameters queryParameters,
0287:                    final boolean returnProxies, final EntityKey keyToRead)
0288:                    throws HibernateException {
0289:
0290:                final int entitySpan = getEntityPersisters().length;
0291:                final List hydratedObjects = entitySpan == 0 ? null
0292:                        : new ArrayList(entitySpan);
0293:
0294:                Object result = null;
0295:                final EntityKey[] loadedKeys = new EntityKey[entitySpan];
0296:
0297:                try {
0298:                    do {
0299:                        Object loaded = getRowFromResultSet(resultSet, session,
0300:                                queryParameters, getLockModes(queryParameters
0301:                                        .getLockModes()), null,
0302:                                hydratedObjects, loadedKeys, returnProxies);
0303:                        if (result == null) {
0304:                            result = loaded;
0305:                        }
0306:                    } while (keyToRead.equals(loadedKeys[0])
0307:                            && resultSet.next());
0308:                } catch (SQLException sqle) {
0309:                    throw JDBCExceptionHelper
0310:                            .convert(
0311:                                    factory.getSQLExceptionConverter(),
0312:                                    sqle,
0313:                                    "could not perform sequential read of results (forward)",
0314:                                    getSQLString());
0315:                }
0316:
0317:                initializeEntitiesAndCollections(hydratedObjects, resultSet,
0318:                        session, queryParameters.isReadOnly());
0319:                session.getPersistenceContext().initializeNonLazyCollections();
0320:                return result;
0321:            }
0322:
0323:            /**
0324:             * Loads a single logical row from the result set moving forward.  This is the
0325:             * processing used from the ScrollableResults where there were collection fetches
0326:             * encountered; thus a single logical row may have multiple rows in the underlying
0327:             * result set.
0328:             *
0329:             * @param resultSet The result set from which to do the load.
0330:             * @param session The session from which the request originated.
0331:             * @param queryParameters The query parameters specified by the user.
0332:             * @param returnProxies Should proxies be generated
0333:             * @return The loaded "row".
0334:             * @throws HibernateException
0335:             */
0336:            public Object loadSequentialRowsForward(final ResultSet resultSet,
0337:                    final SessionImplementor session,
0338:                    final QueryParameters queryParameters,
0339:                    final boolean returnProxies) throws HibernateException {
0340:
0341:                // note that for sequential scrolling, we make the assumption that
0342:                // the first persister element is the "root entity"
0343:
0344:                try {
0345:                    if (resultSet.isAfterLast()) {
0346:                        // don't even bother trying to read further
0347:                        return null;
0348:                    }
0349:
0350:                    if (resultSet.isBeforeFirst()) {
0351:                        resultSet.next();
0352:                    }
0353:
0354:                    // We call getKeyFromResultSet() here so that we can know the
0355:                    // key value upon which to perform the breaking logic.  However,
0356:                    // it is also then called from getRowFromResultSet() which is certainly
0357:                    // not the most efficient.  But the call here is needed, and there
0358:                    // currently is no other way without refactoring of the doQuery()/getRowFromResultSet()
0359:                    // methods
0360:                    final EntityKey currentKey = getKeyFromResultSet(0,
0361:                            getEntityPersisters()[0], null, resultSet, session);
0362:
0363:                    return sequentialLoad(resultSet, session, queryParameters,
0364:                            returnProxies, currentKey);
0365:                } catch (SQLException sqle) {
0366:                    throw JDBCExceptionHelper
0367:                            .convert(
0368:                                    factory.getSQLExceptionConverter(),
0369:                                    sqle,
0370:                                    "could not perform sequential read of results (forward)",
0371:                                    getSQLString());
0372:                }
0373:            }
0374:
0375:            /**
0376:             * Loads a single logical row from the result set moving forward.  This is the
0377:             * processing used from the ScrollableResults where there were collection fetches
0378:             * encountered; thus a single logical row may have multiple rows in the underlying
0379:             * result set.
0380:             *
0381:             * @param resultSet The result set from which to do the load.
0382:             * @param session The session from which the request originated.
0383:             * @param queryParameters The query parameters specified by the user.
0384:             * @param returnProxies Should proxies be generated
0385:             * @return The loaded "row".
0386:             * @throws HibernateException
0387:             */
0388:            public Object loadSequentialRowsReverse(final ResultSet resultSet,
0389:                    final SessionImplementor session,
0390:                    final QueryParameters queryParameters,
0391:                    final boolean returnProxies,
0392:                    final boolean isLogicallyAfterLast)
0393:                    throws HibernateException {
0394:
0395:                // note that for sequential scrolling, we make the assumption that
0396:                // the first persister element is the "root entity"
0397:
0398:                try {
0399:                    if (resultSet.isFirst()) {
0400:                        // don't even bother trying to read any further
0401:                        return null;
0402:                    }
0403:
0404:                    EntityKey keyToRead = null;
0405:                    // This check is needed since processing leaves the cursor
0406:                    // after the last physical row for the current logical row;
0407:                    // thus if we are after the last physical row, this might be
0408:                    // caused by either:
0409:                    //      1) scrolling to the last logical row
0410:                    //      2) scrolling past the last logical row
0411:                    // In the latter scenario, the previous logical row
0412:                    // really is the last logical row.
0413:                    //
0414:                    // In all other cases, we should process back two
0415:                    // logical records (the current logic row, plus the
0416:                    // previous logical row).
0417:                    if (resultSet.isAfterLast() && isLogicallyAfterLast) {
0418:                        // position cursor to the last row
0419:                        resultSet.last();
0420:                        keyToRead = getKeyFromResultSet(0,
0421:                                getEntityPersisters()[0], null, resultSet,
0422:                                session);
0423:                    } else {
0424:                        // Since the result set cursor is always left at the first
0425:                        // physical row after the "last processed", we need to jump
0426:                        // back one position to get the key value we are interested
0427:                        // in skipping
0428:                        resultSet.previous();
0429:
0430:                        // sequentially read the result set in reverse until we recognize
0431:                        // a change in the key value.  At that point, we are pointed at
0432:                        // the last physical sequential row for the logical row in which
0433:                        // we are interested in processing
0434:                        boolean firstPass = true;
0435:                        final EntityKey lastKey = getKeyFromResultSet(0,
0436:                                getEntityPersisters()[0], null, resultSet,
0437:                                session);
0438:                        while (resultSet.previous()) {
0439:                            EntityKey checkKey = getKeyFromResultSet(0,
0440:                                    getEntityPersisters()[0], null, resultSet,
0441:                                    session);
0442:
0443:                            if (firstPass) {
0444:                                firstPass = false;
0445:                                keyToRead = checkKey;
0446:                            }
0447:
0448:                            if (!lastKey.equals(checkKey)) {
0449:                                break;
0450:                            }
0451:                        }
0452:
0453:                    }
0454:
0455:                    // Read backwards until we read past the first physical sequential
0456:                    // row with the key we are interested in loading
0457:                    while (resultSet.previous()) {
0458:                        EntityKey checkKey = getKeyFromResultSet(0,
0459:                                getEntityPersisters()[0], null, resultSet,
0460:                                session);
0461:
0462:                        if (!keyToRead.equals(checkKey)) {
0463:                            break;
0464:                        }
0465:                    }
0466:
0467:                    // Finally, read ahead one row to position result set cursor
0468:                    // at the first physical row we are interested in loading
0469:                    resultSet.next();
0470:
0471:                    // and perform the load
0472:                    return sequentialLoad(resultSet, session, queryParameters,
0473:                            returnProxies, keyToRead);
0474:                } catch (SQLException sqle) {
0475:                    throw JDBCExceptionHelper
0476:                            .convert(
0477:                                    factory.getSQLExceptionConverter(),
0478:                                    sqle,
0479:                                    "could not perform sequential read of results (forward)",
0480:                                    getSQLString());
0481:                }
0482:            }
0483:
0484:            private static EntityKey getOptionalObjectKey(
0485:                    QueryParameters queryParameters, SessionImplementor session) {
0486:                final Object optionalObject = queryParameters
0487:                        .getOptionalObject();
0488:                final Serializable optionalId = queryParameters.getOptionalId();
0489:                final String optionalEntityName = queryParameters
0490:                        .getOptionalEntityName();
0491:
0492:                if (optionalObject != null && optionalEntityName != null) {
0493:                    return new EntityKey(optionalId, session
0494:                            .getEntityPersister(optionalEntityName,
0495:                                    optionalObject), session.getEntityMode());
0496:                } else {
0497:                    return null;
0498:                }
0499:
0500:            }
0501:
0502:            private Object getRowFromResultSet(final ResultSet resultSet,
0503:                    final SessionImplementor session,
0504:                    final QueryParameters queryParameters,
0505:                    final LockMode[] lockModeArray,
0506:                    final EntityKey optionalObjectKey,
0507:                    final List hydratedObjects, final EntityKey[] keys,
0508:                    boolean returnProxies) throws SQLException,
0509:                    HibernateException {
0510:
0511:                final Loadable[] persisters = getEntityPersisters();
0512:                final int entitySpan = persisters.length;
0513:
0514:                for (int i = 0; i < entitySpan; i++) {
0515:                    keys[i] = getKeyFromResultSet(i, persisters[i],
0516:                            i == entitySpan - 1 ? queryParameters
0517:                                    .getOptionalId() : null, resultSet, session);
0518:                    //TODO: the i==entitySpan-1 bit depends upon subclass implementation (very bad)
0519:                }
0520:
0521:                registerNonExists(keys, persisters, session);
0522:
0523:                // this call is side-effecty
0524:                Object[] row = getRow(resultSet, persisters, keys,
0525:                        queryParameters.getOptionalObject(), optionalObjectKey,
0526:                        lockModeArray, hydratedObjects, session);
0527:
0528:                readCollectionElements(row, resultSet, session);
0529:
0530:                if (returnProxies) {
0531:                    // now get an existing proxy for each row element (if there is one)
0532:                    for (int i = 0; i < entitySpan; i++) {
0533:                        Object entity = row[i];
0534:                        Object proxy = session.getPersistenceContext()
0535:                                .proxyFor(persisters[i], keys[i], entity);
0536:                        if (entity != proxy) {
0537:                            // force the proxy to resolve itself
0538:                            ((HibernateProxy) proxy)
0539:                                    .getHibernateLazyInitializer()
0540:                                    .setImplementation(entity);
0541:                            row[i] = proxy;
0542:                        }
0543:                    }
0544:                }
0545:
0546:                return getResultColumnOrRow(row, queryParameters
0547:                        .getResultTransformer(), resultSet, session);
0548:
0549:            }
0550:
0551:            /**
0552:             * Read any collection elements contained in a single row of the result set
0553:             */
0554:            private void readCollectionElements(Object[] row,
0555:                    ResultSet resultSet, SessionImplementor session)
0556:                    throws SQLException, HibernateException {
0557:
0558:                //TODO: make this handle multiple collection roles!
0559:
0560:                final CollectionPersister[] collectionPersisters = getCollectionPersisters();
0561:                if (collectionPersisters != null) {
0562:
0563:                    final CollectionAliases[] descriptors = getCollectionAliases();
0564:                    final int[] collectionOwners = getCollectionOwners();
0565:
0566:                    for (int i = 0; i < collectionPersisters.length; i++) {
0567:
0568:                        final boolean hasCollectionOwners = collectionOwners != null
0569:                                && collectionOwners[i] > -1;
0570:                        //true if this is a query and we are loading multiple instances of the same collection role
0571:                        //otherwise this is a CollectionInitializer and we are loading up a single collection or batch
0572:
0573:                        final Object owner = hasCollectionOwners ? row[collectionOwners[i]]
0574:                                : null; //if null, owner will be retrieved from session
0575:
0576:                        final CollectionPersister collectionPersister = collectionPersisters[i];
0577:                        final Serializable key;
0578:                        if (owner == null) {
0579:                            key = null;
0580:                        } else {
0581:                            key = collectionPersister.getCollectionType()
0582:                                    .getKeyOfOwner(owner, session);
0583:                            //TODO: old version did not require hashmap lookup:
0584:                            //keys[collectionOwner].getIdentifier()
0585:                        }
0586:
0587:                        readCollectionElement(owner, key, collectionPersister,
0588:                                descriptors[i], resultSet, session);
0589:
0590:                    }
0591:
0592:                }
0593:            }
0594:
0595:            private List doQuery(final SessionImplementor session,
0596:                    final QueryParameters queryParameters,
0597:                    final boolean returnProxies) throws SQLException,
0598:                    HibernateException {
0599:
0600:                final RowSelection selection = queryParameters
0601:                        .getRowSelection();
0602:                final int maxRows = hasMaxRows(selection) ? selection
0603:                        .getMaxRows().intValue() : Integer.MAX_VALUE;
0604:
0605:                final int entitySpan = getEntityPersisters().length;
0606:
0607:                final ArrayList hydratedObjects = entitySpan == 0 ? null
0608:                        : new ArrayList(entitySpan * 10);
0609:                final PreparedStatement st = prepareQueryStatement(
0610:                        queryParameters, false, session);
0611:                final ResultSet rs = getResultSet(st, queryParameters
0612:                        .hasAutoDiscoverScalarTypes(), queryParameters
0613:                        .isCallable(), selection, session);
0614:
0615:                // would be great to move all this below here into another method that could also be used
0616:                // from the new scrolling stuff.
0617:                //
0618:                // Would need to change the way the max-row stuff is handled (i.e. behind an interface) so
0619:                // that I could do the control breaking at the means to know when to stop
0620:                final LockMode[] lockModeArray = getLockModes(queryParameters
0621:                        .getLockModes());
0622:                final EntityKey optionalObjectKey = getOptionalObjectKey(
0623:                        queryParameters, session);
0624:
0625:                final boolean createSubselects = isSubselectLoadingEnabled();
0626:                final List subselectResultKeys = createSubselects ? new ArrayList()
0627:                        : null;
0628:                final List results = new ArrayList();
0629:
0630:                try {
0631:
0632:                    handleEmptyCollections(queryParameters.getCollectionKeys(),
0633:                            rs, session);
0634:
0635:                    EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row
0636:
0637:                    if (log.isTraceEnabled())
0638:                        log.trace("processing result set");
0639:
0640:                    int count;
0641:                    for (count = 0; count < maxRows && rs.next(); count++) {
0642:
0643:                        if (log.isTraceEnabled())
0644:                            log.debug("result set row: " + count);
0645:
0646:                        Object result = getRowFromResultSet(rs, session,
0647:                                queryParameters, lockModeArray,
0648:                                optionalObjectKey, hydratedObjects, keys,
0649:                                returnProxies);
0650:                        results.add(result);
0651:
0652:                        if (createSubselects) {
0653:                            subselectResultKeys.add(keys);
0654:                            keys = new EntityKey[entitySpan]; //can't reuse in this case
0655:                        }
0656:
0657:                    }
0658:
0659:                    if (log.isTraceEnabled()) {
0660:                        log.trace("done processing result set (" + count
0661:                                + " rows)");
0662:                    }
0663:
0664:                } finally {
0665:                    session.getBatcher().closeQueryStatement(st, rs);
0666:                }
0667:
0668:                initializeEntitiesAndCollections(hydratedObjects, rs, session,
0669:                        queryParameters.isReadOnly());
0670:
0671:                if (createSubselects)
0672:                    createSubselects(subselectResultKeys, queryParameters,
0673:                            session);
0674:
0675:                return results; //getResultList(results);
0676:
0677:            }
0678:
0679:            protected boolean isSubselectLoadingEnabled() {
0680:                return false;
0681:            }
0682:
0683:            protected boolean hasSubselectLoadableCollections() {
0684:                final Loadable[] loadables = getEntityPersisters();
0685:                for (int i = 0; i < loadables.length; i++) {
0686:                    if (loadables[i].hasSubselectLoadableCollections())
0687:                        return true;
0688:                }
0689:                return false;
0690:            }
0691:
0692:            private static Set[] transpose(List keys) {
0693:                Set[] result = new Set[((EntityKey[]) keys.get(0)).length];
0694:                for (int j = 0; j < result.length; j++) {
0695:                    result[j] = new HashSet(keys.size());
0696:                    for (int i = 0; i < keys.size(); i++) {
0697:                        result[j].add(((EntityKey[]) keys.get(i))[j]);
0698:                    }
0699:                }
0700:                return result;
0701:            }
0702:
0703:            private void createSubselects(List keys,
0704:                    QueryParameters queryParameters, SessionImplementor session) {
0705:                if (keys.size() > 1) { //if we only returned one entity, query by key is more efficient
0706:
0707:                    Set[] keySets = transpose(keys);
0708:
0709:                    Map namedParameterLocMap = buildNamedParameterLocMap(queryParameters);
0710:
0711:                    final Loadable[] loadables = getEntityPersisters();
0712:                    final String[] aliases = getAliases();
0713:                    final Iterator iter = keys.iterator();
0714:                    while (iter.hasNext()) {
0715:
0716:                        final EntityKey[] rowKeys = (EntityKey[]) iter.next();
0717:                        for (int i = 0; i < rowKeys.length; i++) {
0718:
0719:                            if (rowKeys[i] != null
0720:                                    && loadables[i]
0721:                                            .hasSubselectLoadableCollections()) {
0722:
0723:                                SubselectFetch subselectFetch = new SubselectFetch(
0724:                                        //getSQLString(), 
0725:                                        aliases[i], loadables[i],
0726:                                        queryParameters, keySets[i],
0727:                                        namedParameterLocMap);
0728:
0729:                                session.getPersistenceContext()
0730:                                        .getBatchFetchQueue().addSubselect(
0731:                                                rowKeys[i], subselectFetch);
0732:                            }
0733:
0734:                        }
0735:
0736:                    }
0737:                }
0738:            }
0739:
0740:            private Map buildNamedParameterLocMap(
0741:                    QueryParameters queryParameters) {
0742:                if (queryParameters.getNamedParameters() != null) {
0743:                    final Map namedParameterLocMap = new HashMap();
0744:                    Iterator piter = queryParameters.getNamedParameters()
0745:                            .keySet().iterator();
0746:                    while (piter.hasNext()) {
0747:                        String name = (String) piter.next();
0748:                        namedParameterLocMap.put(name,
0749:                                getNamedParameterLocs(name));
0750:                    }
0751:                    return namedParameterLocMap;
0752:                } else {
0753:                    return null;
0754:                }
0755:            }
0756:
0757:            private void initializeEntitiesAndCollections(
0758:                    final List hydratedObjects, final Object resultSetId,
0759:                    final SessionImplementor session, final boolean readOnly)
0760:                    throws HibernateException {
0761:
0762:                final CollectionPersister[] collectionPersisters = getCollectionPersisters();
0763:                if (collectionPersisters != null) {
0764:                    for (int i = 0; i < collectionPersisters.length; i++) {
0765:                        if (collectionPersisters[i].isArray()) {
0766:                            //for arrays, we should end the collection load before resolving
0767:                            //the entities, since the actual array instances are not instantiated
0768:                            //during loading
0769:                            //TODO: or we could do this polymorphically, and have two
0770:                            //      different operations implemented differently for arrays
0771:                            endCollectionLoad(resultSetId, session,
0772:                                    collectionPersisters[i]);
0773:                        }
0774:                    }
0775:                }
0776:
0777:                //important: reuse the same event instances for performance!
0778:                final PreLoadEvent pre;
0779:                final PostLoadEvent post;
0780:                if (session.isEventSource()) {
0781:                    pre = new PreLoadEvent((EventSource) session);
0782:                    post = new PostLoadEvent((EventSource) session);
0783:                } else {
0784:                    pre = null;
0785:                    post = null;
0786:                }
0787:
0788:                if (hydratedObjects != null) {
0789:                    int hydratedObjectsSize = hydratedObjects.size();
0790:                    if (log.isTraceEnabled()) {
0791:                        log.trace("total objects hydrated: "
0792:                                + hydratedObjectsSize);
0793:                    }
0794:                    for (int i = 0; i < hydratedObjectsSize; i++) {
0795:                        TwoPhaseLoad.initializeEntity(hydratedObjects.get(i),
0796:                                readOnly, session, pre, post);
0797:                    }
0798:                }
0799:
0800:                if (collectionPersisters != null) {
0801:                    for (int i = 0; i < collectionPersisters.length; i++) {
0802:                        if (!collectionPersisters[i].isArray()) {
0803:                            //for sets, we should end the collection load after resolving
0804:                            //the entities, since we might call hashCode() on the elements
0805:                            //TODO: or we could do this polymorphically, and have two
0806:                            //      different operations implemented differently for arrays
0807:                            endCollectionLoad(resultSetId, session,
0808:                                    collectionPersisters[i]);
0809:                        }
0810:                    }
0811:                }
0812:
0813:            }
0814:
0815:            private void endCollectionLoad(final Object resultSetId,
0816:                    final SessionImplementor session,
0817:                    final CollectionPersister collectionPersister) {
0818:                //this is a query and we are loading multiple instances of the same collection role
0819:                session.getPersistenceContext().getLoadContexts()
0820:                        .getCollectionLoadContext((ResultSet) resultSetId)
0821:                        .endLoadingCollections(collectionPersister);
0822:            }
0823:
0824:            protected List getResultList(List results,
0825:                    ResultTransformer resultTransformer) throws QueryException {
0826:                return results;
0827:            }
0828:
0829:            /**
0830:             * Get the actual object that is returned in the user-visible result list.
0831:             * This empty implementation merely returns its first argument. This is
0832:             * overridden by some subclasses.
0833:             */
0834:            protected Object getResultColumnOrRow(Object[] row,
0835:                    ResultTransformer transformer, ResultSet rs,
0836:                    SessionImplementor session) throws SQLException,
0837:                    HibernateException {
0838:                return row;
0839:            }
0840:
0841:            /**
0842:             * For missing objects associated by one-to-one with another object in the
0843:             * result set, register the fact that the the object is missing with the
0844:             * session.
0845:             */
0846:            private void registerNonExists(final EntityKey[] keys,
0847:                    final Loadable[] persisters,
0848:                    final SessionImplementor session) {
0849:
0850:                final int[] owners = getOwners();
0851:                if (owners != null) {
0852:
0853:                    EntityType[] ownerAssociationTypes = getOwnerAssociationTypes();
0854:                    for (int i = 0; i < keys.length; i++) {
0855:
0856:                        int owner = owners[i];
0857:                        if (owner > -1) {
0858:                            EntityKey ownerKey = keys[owner];
0859:                            if (keys[i] == null && ownerKey != null) {
0860:
0861:                                final PersistenceContext persistenceContext = session
0862:                                        .getPersistenceContext();
0863:
0864:                                /*final boolean isPrimaryKey;
0865:                                final boolean isSpecialOneToOne;
0866:                                if ( ownerAssociationTypes == null || ownerAssociationTypes[i] == null ) {
0867:                                	isPrimaryKey = true;
0868:                                	isSpecialOneToOne = false;
0869:                                }
0870:                                else {
0871:                                	isPrimaryKey = ownerAssociationTypes[i].getRHSUniqueKeyPropertyName()==null;
0872:                                	isSpecialOneToOne = ownerAssociationTypes[i].getLHSPropertyName()!=null;
0873:                                }*/
0874:
0875:                                //TODO: can we *always* use the "null property" approach for everything?
0876:                                /*if ( isPrimaryKey && !isSpecialOneToOne ) {
0877:                                	persistenceContext.addNonExistantEntityKey( 
0878:                                			new EntityKey( ownerKey.getIdentifier(), persisters[i], session.getEntityMode() ) 
0879:                                	);
0880:                                }
0881:                                else if ( isSpecialOneToOne ) {*/
0882:                                boolean isOneToOneAssociation = ownerAssociationTypes != null
0883:                                        && ownerAssociationTypes[i] != null
0884:                                        && ownerAssociationTypes[i]
0885:                                                .isOneToOne();
0886:                                if (isOneToOneAssociation) {
0887:                                    persistenceContext.addNullProperty(
0888:                                            ownerKey, ownerAssociationTypes[i]
0889:                                                    .getPropertyName());
0890:                                }
0891:                                /*}
0892:                                else {
0893:                                	persistenceContext.addNonExistantEntityUniqueKey( new EntityUniqueKey( 
0894:                                			persisters[i].getEntityName(),
0895:                                			ownerAssociationTypes[i].getRHSUniqueKeyPropertyName(),
0896:                                			ownerKey.getIdentifier(),
0897:                                			persisters[owner].getIdentifierType(),
0898:                                			session.getEntityMode()
0899:                                	) );
0900:                                }*/
0901:                            }
0902:                        }
0903:                    }
0904:                }
0905:            }
0906:
0907:            /**
0908:             * Read one collection element from the current row of the JDBC result set
0909:             */
0910:            private void readCollectionElement(final Object optionalOwner,
0911:                    final Serializable optionalKey,
0912:                    final CollectionPersister persister,
0913:                    final CollectionAliases descriptor, final ResultSet rs,
0914:                    final SessionImplementor session)
0915:                    throws HibernateException, SQLException {
0916:
0917:                final PersistenceContext persistenceContext = session
0918:                        .getPersistenceContext();
0919:
0920:                final Serializable collectionRowKey = (Serializable) persister
0921:                        .readKey(rs, descriptor.getSuffixedKeyAliases(),
0922:                                session);
0923:
0924:                if (collectionRowKey != null) {
0925:                    // we found a collection element in the result set
0926:
0927:                    if (log.isDebugEnabled()) {
0928:                        log.debug("found row of collection: "
0929:                                + MessageHelper.collectionInfoString(persister,
0930:                                        collectionRowKey, getFactory()));
0931:                    }
0932:
0933:                    Object owner = optionalOwner;
0934:                    if (owner == null) {
0935:                        owner = persistenceContext.getCollectionOwner(
0936:                                collectionRowKey, persister);
0937:                        if (owner == null) {
0938:                            //TODO: This is assertion is disabled because there is a bug that means the
0939:                            //	  original owner of a transient, uninitialized collection is not known
0940:                            //	  if the collection is re-referenced by a different object associated
0941:                            //	  with the current Session
0942:                            //throw new AssertionFailure("bug loading unowned collection");
0943:                        }
0944:                    }
0945:
0946:                    PersistentCollection rowCollection = persistenceContext
0947:                            .getLoadContexts().getCollectionLoadContext(rs)
0948:                            .getLoadingCollection(persister, collectionRowKey);
0949:
0950:                    if (rowCollection != null) {
0951:                        rowCollection
0952:                                .readFrom(rs, persister, descriptor, owner);
0953:                    }
0954:
0955:                } else if (optionalKey != null) {
0956:                    // we did not find a collection element in the result set, so we
0957:                    // ensure that a collection is created with the owner's identifier,
0958:                    // since what we have is an empty collection
0959:
0960:                    if (log.isDebugEnabled()) {
0961:                        log
0962:                                .debug("result set contains (possibly empty) collection: "
0963:                                        + MessageHelper.collectionInfoString(
0964:                                                persister, optionalKey,
0965:                                                getFactory()));
0966:                    }
0967:
0968:                    persistenceContext.getLoadContexts()
0969:                            .getCollectionLoadContext(rs).getLoadingCollection(
0970:                                    persister, optionalKey); // handle empty collection
0971:                }
0972:
0973:                // else no collection element, but also no owner
0974:
0975:            }
0976:
0977:            /**
0978:             * If this is a collection initializer, we need to tell the session that a collection
0979:             * is being initilized, to account for the possibility of the collection having
0980:             * no elements (hence no rows in the result set).
0981:             */
0982:            private void handleEmptyCollections(final Serializable[] keys,
0983:                    final Object resultSetId, final SessionImplementor session) {
0984:
0985:                if (keys != null) {
0986:                    // this is a collection initializer, so we must create a collection
0987:                    // for each of the passed-in keys, to account for the possibility
0988:                    // that the collection is empty and has no rows in the result set
0989:
0990:                    CollectionPersister[] collectionPersisters = getCollectionPersisters();
0991:                    for (int j = 0; j < collectionPersisters.length; j++) {
0992:                        for (int i = 0; i < keys.length; i++) {
0993:                            //handle empty collections
0994:
0995:                            if (log.isDebugEnabled()) {
0996:                                log
0997:                                        .debug("result set contains (possibly empty) collection: "
0998:                                                + MessageHelper
0999:                                                        .collectionInfoString(
1000:                                                                collectionPersisters[j],
1001:                                                                keys[i],
1002:                                                                getFactory()));
1003:                            }
1004:
1005:                            session.getPersistenceContext().getLoadContexts()
1006:                                    .getCollectionLoadContext(
1007:                                            (ResultSet) resultSetId)
1008:                                    .getLoadingCollection(
1009:                                            collectionPersisters[j], keys[i]);
1010:                        }
1011:                    }
1012:                }
1013:
1014:                // else this is not a collection initializer (and empty collections will
1015:                // be detected by looking for the owner's identifier in the result set)
1016:            }
1017:
1018:            /**
1019:             * Read a row of <tt>Key</tt>s from the <tt>ResultSet</tt> into the given array.
1020:             * Warning: this method is side-effecty.
1021:             * <p/>
1022:             * If an <tt>id</tt> is given, don't bother going to the <tt>ResultSet</tt>.
1023:             */
1024:            private EntityKey getKeyFromResultSet(final int i,
1025:                    final Loadable persister, final Serializable id,
1026:                    final ResultSet rs, final SessionImplementor session)
1027:                    throws HibernateException, SQLException {
1028:
1029:                Serializable resultId;
1030:
1031:                // if we know there is exactly 1 row, we can skip.
1032:                // it would be great if we could _always_ skip this;
1033:                // it is a problem for <key-many-to-one>
1034:
1035:                if (isSingleRowLoader() && id != null) {
1036:                    resultId = id;
1037:                } else {
1038:
1039:                    Type idType = persister.getIdentifierType();
1040:                    resultId = (Serializable) idType.nullSafeGet(rs,
1041:                            getEntityAliases()[i].getSuffixedKeyAliases(),
1042:                            session, null //problematic for <key-many-to-one>!
1043:                            );
1044:
1045:                    final boolean idIsResultId = id != null
1046:                            && resultId != null
1047:                            && idType.isEqual(id, resultId, session
1048:                                    .getEntityMode(), factory);
1049:
1050:                    if (idIsResultId)
1051:                        resultId = id; //use the id passed in
1052:                }
1053:
1054:                return resultId == null ? null : new EntityKey(resultId,
1055:                        persister, session.getEntityMode());
1056:            }
1057:
1058:            /**
1059:             * Check the version of the object in the <tt>ResultSet</tt> against
1060:             * the object version in the session cache, throwing an exception
1061:             * if the version numbers are different
1062:             */
1063:            private void checkVersion(final int i, final Loadable persister,
1064:                    final Serializable id, final Object entity,
1065:                    final ResultSet rs, final SessionImplementor session)
1066:                    throws HibernateException, SQLException {
1067:
1068:                Object version = session.getPersistenceContext().getEntry(
1069:                        entity).getVersion();
1070:
1071:                if (version != null) { //null version means the object is in the process of being loaded somewhere else in the ResultSet
1072:                    VersionType versionType = persister.getVersionType();
1073:                    Object currentVersion = versionType.nullSafeGet(rs,
1074:                            getEntityAliases()[i].getSuffixedVersionAliases(),
1075:                            session, null);
1076:                    if (!versionType.isEqual(version, currentVersion)) {
1077:                        if (session.getFactory().getStatistics()
1078:                                .isStatisticsEnabled()) {
1079:                            session.getFactory().getStatisticsImplementor()
1080:                                    .optimisticFailure(
1081:                                            persister.getEntityName());
1082:                        }
1083:                        throw new StaleObjectStateException(persister
1084:                                .getEntityName(), id);
1085:                    }
1086:                }
1087:
1088:            }
1089:
1090:            /**
1091:             * Resolve any ids for currently loaded objects, duplications within the
1092:             * <tt>ResultSet</tt>, etc. Instantiate empty objects to be initialized from the
1093:             * <tt>ResultSet</tt>. Return an array of objects (a row of results) and an
1094:             * array of booleans (by side-effect) that determine whether the corresponding
1095:             * object should be initialized.
1096:             */
1097:            private Object[] getRow(final ResultSet rs,
1098:                    final Loadable[] persisters, final EntityKey[] keys,
1099:                    final Object optionalObject,
1100:                    final EntityKey optionalObjectKey,
1101:                    final LockMode[] lockModes, final List hydratedObjects,
1102:                    final SessionImplementor session)
1103:                    throws HibernateException, SQLException {
1104:
1105:                final int cols = persisters.length;
1106:                final EntityAliases[] descriptors = getEntityAliases();
1107:
1108:                if (log.isDebugEnabled()) {
1109:                    log.debug("result row: " + StringHelper.toString(keys));
1110:                }
1111:
1112:                final Object[] rowResults = new Object[cols];
1113:
1114:                for (int i = 0; i < cols; i++) {
1115:
1116:                    Object object = null;
1117:                    EntityKey key = keys[i];
1118:
1119:                    if (keys[i] == null) {
1120:                        //do nothing
1121:                    } else {
1122:
1123:                        //If the object is already loaded, return the loaded one
1124:                        object = session.getEntityUsingInterceptor(key);
1125:                        if (object != null) {
1126:                            //its already loaded so don't need to hydrate it
1127:                            instanceAlreadyLoaded(rs, i, persisters[i], key,
1128:                                    object, lockModes[i], session);
1129:                        } else {
1130:                            object = instanceNotYetLoaded(rs, i, persisters[i],
1131:                                    descriptors[i].getRowIdAlias(), key,
1132:                                    lockModes[i], optionalObjectKey,
1133:                                    optionalObject, hydratedObjects, session);
1134:                        }
1135:
1136:                    }
1137:
1138:                    rowResults[i] = object;
1139:
1140:                }
1141:
1142:                return rowResults;
1143:            }
1144:
1145:            /**
1146:             * The entity instance is already in the session cache
1147:             */
1148:            private void instanceAlreadyLoaded(final ResultSet rs, final int i,
1149:                    final Loadable persister, final EntityKey key,
1150:                    final Object object, final LockMode lockMode,
1151:                    final SessionImplementor session)
1152:                    throws HibernateException, SQLException {
1153:
1154:                if (!persister.isInstance(object, session.getEntityMode())) {
1155:                    throw new WrongClassException(
1156:                            "loaded object was of wrong class "
1157:                                    + object.getClass(), key.getIdentifier(),
1158:                            persister.getEntityName());
1159:                }
1160:
1161:                if (LockMode.NONE != lockMode && upgradeLocks()) { //no point doing this if NONE was requested
1162:
1163:                    final boolean isVersionCheckNeeded = persister
1164:                            .isVersioned()
1165:                            && session.getPersistenceContext().getEntry(object)
1166:                                    .getLockMode().lessThan(lockMode);
1167:                    // we don't need to worry about existing version being uninitialized
1168:                    // because this block isn't called by a re-entrant load (re-entrant
1169:                    // loads _always_ have lock mode NONE)
1170:                    if (isVersionCheckNeeded) {
1171:                        //we only check the version when _upgrading_ lock modes
1172:                        checkVersion(i, persister, key.getIdentifier(), object,
1173:                                rs, session);
1174:                        //we need to upgrade the lock mode to the mode requested
1175:                        session.getPersistenceContext().getEntry(object)
1176:                                .setLockMode(lockMode);
1177:                    }
1178:                }
1179:            }
1180:
1181:            /**
1182:             * The entity instance is not in the session cache
1183:             */
1184:            private Object instanceNotYetLoaded(final ResultSet rs,
1185:                    final int i, final Loadable persister,
1186:                    final String rowIdAlias, final EntityKey key,
1187:                    final LockMode lockMode, final EntityKey optionalObjectKey,
1188:                    final Object optionalObject, final List hydratedObjects,
1189:                    final SessionImplementor session)
1190:                    throws HibernateException, SQLException {
1191:
1192:                final String instanceClass = getInstanceClass(rs, i, persister,
1193:                        key.getIdentifier(), session);
1194:
1195:                final Object object;
1196:                if (optionalObjectKey != null && key.equals(optionalObjectKey)) {
1197:                    //its the given optional object
1198:                    object = optionalObject;
1199:                } else {
1200:                    // instantiate a new instance
1201:                    object = session.instantiate(instanceClass, key
1202:                            .getIdentifier());
1203:                }
1204:
1205:                //need to hydrate it.
1206:
1207:                // grab its state from the ResultSet and keep it in the Session
1208:                // (but don't yet initialize the object itself)
1209:                // note that we acquire LockMode.READ even if it was not requested
1210:                LockMode acquiredLockMode = lockMode == LockMode.NONE ? LockMode.READ
1211:                        : lockMode;
1212:                loadFromResultSet(rs, i, object, instanceClass, key,
1213:                        rowIdAlias, acquiredLockMode, persister, session);
1214:
1215:                //materialize associations (and initialize the object) later
1216:                hydratedObjects.add(object);
1217:
1218:                return object;
1219:            }
1220:
1221:            private boolean isEagerPropertyFetchEnabled(int i) {
1222:                boolean[] array = getEntityEagerPropertyFetches();
1223:                return array != null && array[i];
1224:            }
1225:
1226:            /**
1227:             * Hydrate the state an object from the SQL <tt>ResultSet</tt>, into
1228:             * an array or "hydrated" values (do not resolve associations yet),
1229:             * and pass the hydrates state to the session.
1230:             */
1231:            private void loadFromResultSet(final ResultSet rs, final int i,
1232:                    final Object object, final String instanceEntityName,
1233:                    final EntityKey key, final String rowIdAlias,
1234:                    final LockMode lockMode, final Loadable rootPersister,
1235:                    final SessionImplementor session) throws SQLException,
1236:                    HibernateException {
1237:
1238:                final Serializable id = key.getIdentifier();
1239:
1240:                // Get the persister for the _subclass_
1241:                final Loadable persister = (Loadable) getFactory()
1242:                        .getEntityPersister(instanceEntityName);
1243:
1244:                if (log.isTraceEnabled()) {
1245:                    log.trace("Initializing object from ResultSet: "
1246:                            + MessageHelper.infoString(persister, id,
1247:                                    getFactory()));
1248:                }
1249:
1250:                boolean eagerPropertyFetch = isEagerPropertyFetchEnabled(i);
1251:
1252:                // add temp entry so that the next step is circular-reference
1253:                // safe - only needed because some types don't take proper
1254:                // advantage of two-phase-load (esp. components)
1255:                TwoPhaseLoad.addUninitializedEntity(key, object, persister,
1256:                        lockMode, !eagerPropertyFetch, session);
1257:
1258:                //This is not very nice (and quite slow):
1259:                final String[][] cols = persister == rootPersister ? getEntityAliases()[i]
1260:                        .getSuffixedPropertyAliases()
1261:                        : getEntityAliases()[i]
1262:                                .getSuffixedPropertyAliases(persister);
1263:
1264:                final Object[] values = persister.hydrate(rs, id, object,
1265:                        rootPersister, cols, eagerPropertyFetch, session);
1266:
1267:                final Object rowId = persister.hasRowId() ? rs
1268:                        .getObject(rowIdAlias) : null;
1269:
1270:                final AssociationType[] ownerAssociationTypes = getOwnerAssociationTypes();
1271:                if (ownerAssociationTypes != null
1272:                        && ownerAssociationTypes[i] != null) {
1273:                    String ukName = ownerAssociationTypes[i]
1274:                            .getRHSUniqueKeyPropertyName();
1275:                    if (ukName != null) {
1276:                        final int index = ((UniqueKeyLoadable) persister)
1277:                                .getPropertyIndex(ukName);
1278:                        final Type type = persister.getPropertyTypes()[index];
1279:
1280:                        // polymorphism not really handled completely correctly,
1281:                        // perhaps...well, actually its ok, assuming that the
1282:                        // entity name used in the lookup is the same as the
1283:                        // the one used here, which it will be
1284:
1285:                        EntityUniqueKey euk = new EntityUniqueKey(rootPersister
1286:                                .getEntityName(), //polymorphism comment above
1287:                                ukName, type.semiResolve(values[index],
1288:                                        session, object), type, session
1289:                                        .getEntityMode(), session.getFactory());
1290:                        session.getPersistenceContext().addEntity(euk, object);
1291:                    }
1292:                }
1293:
1294:                TwoPhaseLoad.postHydrate(persister, id, values, rowId, object,
1295:                        lockMode, !eagerPropertyFetch, session);
1296:
1297:            }
1298:
1299:            /**
1300:             * Determine the concrete class of an instance in the <tt>ResultSet</tt>
1301:             */
1302:            private String getInstanceClass(final ResultSet rs, final int i,
1303:                    final Loadable persister, final Serializable id,
1304:                    final SessionImplementor session)
1305:                    throws HibernateException, SQLException {
1306:
1307:                if (persister.hasSubclasses()) {
1308:
1309:                    // Code to handle subclasses of topClass
1310:                    Object discriminatorValue = persister
1311:                            .getDiscriminatorType().nullSafeGet(
1312:                                    rs,
1313:                                    getEntityAliases()[i]
1314:                                            .getSuffixedDiscriminatorAlias(),
1315:                                    session, null);
1316:
1317:                    final String result = persister
1318:                            .getSubclassForDiscriminatorValue(discriminatorValue);
1319:
1320:                    if (result == null) {
1321:                        //woops we got an instance of another class hierarchy branch
1322:                        throw new WrongClassException("Discriminator: "
1323:                                + discriminatorValue, id, persister
1324:                                .getEntityName());
1325:                    }
1326:
1327:                    return result;
1328:
1329:                } else {
1330:                    return persister.getEntityName();
1331:                }
1332:            }
1333:
1334:            /**
1335:             * Advance the cursor to the first required row of the <tt>ResultSet</tt>
1336:             */
1337:            private void advance(final ResultSet rs,
1338:                    final RowSelection selection) throws SQLException {
1339:
1340:                final int firstRow = getFirstRow(selection);
1341:                if (firstRow != 0) {
1342:                    if (getFactory().getSettings()
1343:                            .isScrollableResultSetsEnabled()) {
1344:                        // we can go straight to the first required row
1345:                        rs.absolute(firstRow);
1346:                    } else {
1347:                        // we need to step through the rows one row at a time (slow)
1348:                        for (int m = 0; m < firstRow; m++)
1349:                            rs.next();
1350:                    }
1351:                }
1352:            }
1353:
1354:            private static boolean hasMaxRows(RowSelection selection) {
1355:                return selection != null && selection.getMaxRows() != null;
1356:            }
1357:
1358:            private static int getFirstRow(RowSelection selection) {
1359:                if (selection == null || selection.getFirstRow() == null) {
1360:                    return 0;
1361:                } else {
1362:                    return selection.getFirstRow().intValue();
1363:                }
1364:            }
1365:
1366:            /**
1367:             * Should we pre-process the SQL string, adding a dialect-specific
1368:             * LIMIT clause.
1369:             */
1370:            private static boolean useLimit(final RowSelection selection,
1371:                    final Dialect dialect) {
1372:                return dialect.supportsLimit() && hasMaxRows(selection);
1373:            }
1374:
1375:            /**
1376:             * Obtain a <tt>PreparedStatement</tt> with all parameters pre-bound.
1377:             * Bind JDBC-style <tt>?</tt> parameters, named parameters, and
1378:             * limit parameters.
1379:             */
1380:            protected final PreparedStatement prepareQueryStatement(
1381:                    final QueryParameters queryParameters,
1382:                    final boolean scroll, final SessionImplementor session)
1383:                    throws SQLException, HibernateException {
1384:                String sql = processFilters(queryParameters, session);
1385:                final Dialect dialect = getFactory().getDialect();
1386:                final RowSelection selection = queryParameters
1387:                        .getRowSelection();
1388:                boolean useLimit = useLimit(selection, dialect);
1389:                boolean hasFirstRow = getFirstRow(selection) > 0;
1390:                boolean useOffset = hasFirstRow && useLimit
1391:                        && dialect.supportsLimitOffset();
1392:                boolean callable = queryParameters.isCallable();
1393:
1394:                boolean useScrollableResultSetToSkip = hasFirstRow
1395:                        && !useOffset
1396:                        && getFactory().getSettings()
1397:                                .isScrollableResultSetsEnabled();
1398:                ScrollMode scrollMode = scroll ? queryParameters
1399:                        .getScrollMode() : ScrollMode.SCROLL_INSENSITIVE;
1400:
1401:                if (useLimit) {
1402:                    sql = dialect.getLimitString(
1403:                            sql.trim(), //use of trim() here is ugly?
1404:                            useOffset ? getFirstRow(selection) : 0,
1405:                            getMaxOrLimit(selection, dialect));
1406:                }
1407:
1408:                sql = preprocessSQL(sql, queryParameters, dialect);
1409:
1410:                PreparedStatement st = null;
1411:
1412:                if (callable) {
1413:                    st = session.getBatcher().prepareCallableQueryStatement(
1414:                            sql, scroll || useScrollableResultSetToSkip,
1415:                            scrollMode);
1416:                } else {
1417:                    st = session.getBatcher().prepareQueryStatement(sql,
1418:                            scroll || useScrollableResultSetToSkip, scrollMode);
1419:                }
1420:
1421:                try {
1422:
1423:                    int col = 1;
1424:                    //TODO: can we limit stored procedures ?!
1425:                    if (useLimit && dialect.bindLimitParametersFirst()) {
1426:                        col += bindLimitParameters(st, col, selection);
1427:                    }
1428:                    if (callable) {
1429:                        col = dialect.registerResultSetOutParameter(
1430:                                (CallableStatement) st, col);
1431:                    }
1432:
1433:                    col += bindParameterValues(st, queryParameters, col,
1434:                            session);
1435:
1436:                    if (useLimit && !dialect.bindLimitParametersFirst()) {
1437:                        col += bindLimitParameters(st, col, selection);
1438:                    }
1439:
1440:                    if (!useLimit) {
1441:                        setMaxRows(st, selection);
1442:                    }
1443:
1444:                    if (selection != null) {
1445:                        if (selection.getTimeout() != null) {
1446:                            st.setQueryTimeout(selection.getTimeout()
1447:                                    .intValue());
1448:                        }
1449:                        if (selection.getFetchSize() != null) {
1450:                            st
1451:                                    .setFetchSize(selection.getFetchSize()
1452:                                            .intValue());
1453:                        }
1454:                    }
1455:                } catch (SQLException sqle) {
1456:                    session.getBatcher().closeQueryStatement(st, null);
1457:                    throw sqle;
1458:                } catch (HibernateException he) {
1459:                    session.getBatcher().closeQueryStatement(st, null);
1460:                    throw he;
1461:                }
1462:
1463:                return st;
1464:            }
1465:
1466:            protected String processFilters(QueryParameters queryParameters,
1467:                    SessionImplementor session) {
1468:                queryParameters.processFilters(getSQLString(), session);
1469:                return queryParameters.getFilteredSQL();
1470:            }
1471:
1472:            /**
1473:             * Some dialect-specific LIMIT clauses require the maximium last row number
1474:             * (aka, first_row_number + total_row_count), while others require the maximum
1475:             * returned row count (the total maximum number of rows to return).
1476:             *
1477:             * @param selection The selection criteria
1478:             * @param dialect The dialect
1479:             * @return The appropriate value to bind into the limit clause.
1480:             */
1481:            private static int getMaxOrLimit(final RowSelection selection,
1482:                    final Dialect dialect) {
1483:                final int firstRow = getFirstRow(selection);
1484:                final int lastRow = selection.getMaxRows().intValue();
1485:                if (dialect.useMaxForLimit()) {
1486:                    return lastRow + firstRow;
1487:                } else {
1488:                    return lastRow;
1489:                }
1490:            }
1491:
1492:            /**
1493:             * Bind parameter values needed by the dialect-specific LIMIT clause.
1494:             *
1495:             * @param statement The statement to which to bind limit param values.
1496:             * @param index The bind position from which to start binding
1497:             * @param selection The selection object containing the limit information.
1498:             * @return The number of parameter values bound.
1499:             * @throws java.sql.SQLException Indicates problems binding parameter values.
1500:             */
1501:            private int bindLimitParameters(final PreparedStatement statement,
1502:                    final int index, final RowSelection selection)
1503:                    throws SQLException {
1504:                Dialect dialect = getFactory().getDialect();
1505:                if (!dialect.supportsVariableLimit()) {
1506:                    return 0;
1507:                }
1508:                if (!hasMaxRows(selection)) {
1509:                    throw new AssertionFailure("no max results set");
1510:                }
1511:                int firstRow = getFirstRow(selection);
1512:                int lastRow = getMaxOrLimit(selection, dialect);
1513:                boolean hasFirstRow = firstRow > 0
1514:                        && dialect.supportsLimitOffset();
1515:                boolean reverse = dialect.bindLimitParametersInReverseOrder();
1516:                if (hasFirstRow) {
1517:                    statement.setInt(index + (reverse ? 1 : 0), firstRow);
1518:                }
1519:                statement.setInt(index + (reverse || !hasFirstRow ? 0 : 1),
1520:                        lastRow);
1521:                return hasFirstRow ? 2 : 1;
1522:            }
1523:
1524:            /**
1525:             * Use JDBC API to limit the number of rows returned by the SQL query if necessary
1526:             */
1527:            private void setMaxRows(final PreparedStatement st,
1528:                    final RowSelection selection) throws SQLException {
1529:                if (hasMaxRows(selection)) {
1530:                    st.setMaxRows(selection.getMaxRows().intValue()
1531:                            + getFirstRow(selection));
1532:                }
1533:            }
1534:
1535:            /**
1536:             * Bind all parameter values into the prepared statement in preparation
1537:             * for execution.
1538:             *
1539:             * @param statement The JDBC prepared statement
1540:             * @param queryParameters The encapsulation of the parameter values to be bound.
1541:             * @param startIndex The position from which to start binding parameter values.
1542:             * @param session The originating session.
1543:             * @return The number of JDBC bind positions actually bound during this method execution.
1544:             * @throws SQLException Indicates problems performing the binding.
1545:             */
1546:            protected int bindParameterValues(PreparedStatement statement,
1547:                    QueryParameters queryParameters, int startIndex,
1548:                    SessionImplementor session) throws SQLException {
1549:                int span = 0;
1550:                span += bindPositionalParameters(statement, queryParameters,
1551:                        startIndex, session);
1552:                span += bindNamedParameters(statement, queryParameters
1553:                        .getNamedParameters(), startIndex + span, session);
1554:                return span;
1555:            }
1556:
1557:            /**
1558:             * Bind positional parameter values to the JDBC prepared statement.
1559:             * <p/>
1560:             * Postional parameters are those specified by JDBC-style ? parameters
1561:             * in the source query.  It is (currently) expected that these come
1562:             * before any named parameters in the source query.
1563:             *
1564:             * @param statement The JDBC prepared statement
1565:             * @param queryParameters The encapsulation of the parameter values to be bound.
1566:             * @param startIndex The position from which to start binding parameter values.
1567:             * @param session The originating session.
1568:             * @return The number of JDBC bind positions actually bound during this method execution.
1569:             * @throws SQLException Indicates problems performing the binding.
1570:             * @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
1571:             */
1572:            protected int bindPositionalParameters(
1573:                    final PreparedStatement statement,
1574:                    final QueryParameters queryParameters,
1575:                    final int startIndex, final SessionImplementor session)
1576:                    throws SQLException, HibernateException {
1577:                final Object[] values = queryParameters
1578:                        .getFilteredPositionalParameterValues();
1579:                final Type[] types = queryParameters
1580:                        .getFilteredPositionalParameterTypes();
1581:                int span = 0;
1582:                for (int i = 0; i < values.length; i++) {
1583:                    types[i].nullSafeSet(statement, values[i], startIndex
1584:                            + span, session);
1585:                    span += types[i].getColumnSpan(getFactory());
1586:                }
1587:                return span;
1588:            }
1589:
1590:            /**
1591:             * Bind named parameters to the JDBC prepared statement.
1592:             * <p/>
1593:             * This is a generic implementation, the problem being that in the
1594:             * general case we do not know enough information about the named
1595:             * parameters to perform this in a complete manner here.  Thus this
1596:             * is generally overridden on subclasses allowing named parameters to
1597:             * apply the specific behavior.  The most usual limitation here is that
1598:             * we need to assume the type span is always one...
1599:             *
1600:             * @param statement The JDBC prepared statement
1601:             * @param namedParams A map of parameter names to values
1602:             * @param startIndex The position from which to start binding parameter values.
1603:             * @param session The originating session.
1604:             * @return The number of JDBC bind positions actually bound during this method execution.
1605:             * @throws SQLException Indicates problems performing the binding.
1606:             * @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
1607:             */
1608:            protected int bindNamedParameters(
1609:                    final PreparedStatement statement, final Map namedParams,
1610:                    final int startIndex, final SessionImplementor session)
1611:                    throws SQLException, HibernateException {
1612:                if (namedParams != null) {
1613:                    // assumes that types are all of span 1
1614:                    Iterator iter = namedParams.entrySet().iterator();
1615:                    int result = 0;
1616:                    while (iter.hasNext()) {
1617:                        Map.Entry e = (Map.Entry) iter.next();
1618:                        String name = (String) e.getKey();
1619:                        TypedValue typedval = (TypedValue) e.getValue();
1620:                        int[] locs = getNamedParameterLocs(name);
1621:                        for (int i = 0; i < locs.length; i++) {
1622:                            if (log.isDebugEnabled()) {
1623:                                log.debug("bindNamedParameters() "
1624:                                        + typedval.getValue() + " -> " + name
1625:                                        + " [" + (locs[i] + startIndex) + "]");
1626:                            }
1627:                            typedval.getType().nullSafeSet(statement,
1628:                                    typedval.getValue(), locs[i] + startIndex,
1629:                                    session);
1630:                        }
1631:                        result += locs.length;
1632:                    }
1633:                    return result;
1634:                } else {
1635:                    return 0;
1636:                }
1637:            }
1638:
1639:            public int[] getNamedParameterLocs(String name) {
1640:                throw new AssertionFailure("no named parameters");
1641:            }
1642:
1643:            /**
1644:             * Fetch a <tt>PreparedStatement</tt>, call <tt>setMaxRows</tt> and then execute it,
1645:             * advance to the first result and return an SQL <tt>ResultSet</tt>
1646:             */
1647:            protected final ResultSet getResultSet(final PreparedStatement st,
1648:                    final boolean autodiscovertypes, final boolean callable,
1649:                    final RowSelection selection,
1650:                    final SessionImplementor session) throws SQLException,
1651:                    HibernateException {
1652:
1653:                ResultSet rs = null;
1654:                try {
1655:                    Dialect dialect = getFactory().getDialect();
1656:                    if (callable) {
1657:                        rs = session.getBatcher().getResultSet(
1658:                                (CallableStatement) st, dialect);
1659:                    } else {
1660:                        rs = session.getBatcher().getResultSet(st);
1661:                    }
1662:                    rs = wrapResultSetIfEnabled(rs, session);
1663:
1664:                    if (!dialect.supportsLimitOffset()
1665:                            || !useLimit(selection, dialect)) {
1666:                        advance(rs, selection);
1667:                    }
1668:
1669:                    if (autodiscovertypes) {
1670:                        autoDiscoverTypes(rs);
1671:                    }
1672:                    return rs;
1673:                } catch (SQLException sqle) {
1674:                    session.getBatcher().closeQueryStatement(st, rs);
1675:                    throw sqle;
1676:                }
1677:            }
1678:
1679:            protected void autoDiscoverTypes(ResultSet rs) {
1680:                throw new AssertionFailure(
1681:                        "Auto discover types not supported in this loader");
1682:
1683:            }
1684:
1685:            private synchronized ResultSet wrapResultSetIfEnabled(
1686:                    final ResultSet rs, final SessionImplementor session) {
1687:                // synchronized to avoid multi-thread access issues; defined as method synch to avoid
1688:                // potential deadlock issues due to nature of code.
1689:                if (session.getFactory().getSettings()
1690:                        .isWrapResultSetsEnabled()) {
1691:                    try {
1692:                        log.debug("Wrapping result set [" + rs + "]");
1693:                        return new ResultSetWrapper(rs,
1694:                                retreiveColumnNameToIndexCache(rs));
1695:                    } catch (SQLException e) {
1696:                        log.info("Error wrapping result set", e);
1697:                        return rs;
1698:                    }
1699:                } else {
1700:                    return rs;
1701:                }
1702:            }
1703:
1704:            private ColumnNameCache retreiveColumnNameToIndexCache(ResultSet rs)
1705:                    throws SQLException {
1706:                if (columnNameCache == null) {
1707:                    log.trace("Building columnName->columnIndex cache");
1708:                    columnNameCache = new ColumnNameCache(rs.getMetaData()
1709:                            .getColumnCount());
1710:                }
1711:
1712:                return columnNameCache;
1713:            }
1714:
1715:            /**
1716:             * Called by subclasses that load entities
1717:             * @param persister only needed for logging
1718:             */
1719:            protected final List loadEntity(final SessionImplementor session,
1720:                    final Object id, final Type identifierType,
1721:                    final Object optionalObject,
1722:                    final String optionalEntityName,
1723:                    final Serializable optionalIdentifier,
1724:                    final EntityPersister persister) throws HibernateException {
1725:
1726:                if (log.isDebugEnabled()) {
1727:                    log.debug("loading entity: "
1728:                            + MessageHelper.infoString(persister, id,
1729:                                    identifierType, getFactory()));
1730:                }
1731:
1732:                List result;
1733:                try {
1734:                    result = doQueryAndInitializeNonLazyCollections(session,
1735:                            new QueryParameters(new Type[] { identifierType },
1736:                                    new Object[] { id }, optionalObject,
1737:                                    optionalEntityName, optionalIdentifier),
1738:                            false);
1739:                } catch (SQLException sqle) {
1740:                    final Loadable[] persisters = getEntityPersisters();
1741:                    throw JDBCExceptionHelper.convert(factory
1742:                            .getSQLExceptionConverter(), sqle,
1743:                            "could not load an entity: "
1744:                                    + MessageHelper.infoString(
1745:                                            persisters[persisters.length - 1],
1746:                                            id, identifierType, getFactory()),
1747:                            getSQLString());
1748:                }
1749:
1750:                log.debug("done entity load");
1751:
1752:                return result;
1753:
1754:            }
1755:
1756:            /**
1757:             * Called by subclasses that load entities
1758:             * @param persister only needed for logging
1759:             */
1760:            protected final List loadEntity(final SessionImplementor session,
1761:                    final Object key, final Object index, final Type keyType,
1762:                    final Type indexType, final EntityPersister persister)
1763:                    throws HibernateException {
1764:
1765:                if (log.isDebugEnabled()) {
1766:                    log.debug("loading collection element by index");
1767:                }
1768:
1769:                List result;
1770:                try {
1771:                    result = doQueryAndInitializeNonLazyCollections(session,
1772:                            new QueryParameters(
1773:                                    new Type[] { keyType, indexType },
1774:                                    new Object[] { key, index }), false);
1775:                } catch (SQLException sqle) {
1776:                    throw JDBCExceptionHelper.convert(factory
1777:                            .getSQLExceptionConverter(), sqle,
1778:                            "could not collection element by index",
1779:                            getSQLString());
1780:                }
1781:
1782:                log.debug("done entity load");
1783:
1784:                return result;
1785:
1786:            }
1787:
1788:            /**
1789:             * Called by wrappers that batch load entities
1790:             * @param persister only needed for logging
1791:             */
1792:            public final List loadEntityBatch(final SessionImplementor session,
1793:                    final Serializable[] ids, final Type idType,
1794:                    final Object optionalObject,
1795:                    final String optionalEntityName,
1796:                    final Serializable optionalId,
1797:                    final EntityPersister persister) throws HibernateException {
1798:
1799:                if (log.isDebugEnabled()) {
1800:                    log.debug("batch loading entity: "
1801:                            + MessageHelper.infoString(persister, ids,
1802:                                    getFactory()));
1803:                }
1804:
1805:                Type[] types = new Type[ids.length];
1806:                Arrays.fill(types, idType);
1807:                List result;
1808:                try {
1809:                    result = doQueryAndInitializeNonLazyCollections(session,
1810:                            new QueryParameters(types, ids, optionalObject,
1811:                                    optionalEntityName, optionalId), false);
1812:                } catch (SQLException sqle) {
1813:                    throw JDBCExceptionHelper.convert(factory
1814:                            .getSQLExceptionConverter(), sqle,
1815:                            "could not load an entity batch: "
1816:                                    + MessageHelper.infoString(
1817:                                            getEntityPersisters()[0], ids,
1818:                                            getFactory()), getSQLString());
1819:                }
1820:
1821:                log.debug("done entity batch load");
1822:
1823:                return result;
1824:
1825:            }
1826:
1827:            /**
1828:             * Called by subclasses that initialize collections
1829:             */
1830:            public final void loadCollection(final SessionImplementor session,
1831:                    final Serializable id, final Type type)
1832:                    throws HibernateException {
1833:
1834:                if (log.isDebugEnabled()) {
1835:                    log.debug("loading collection: "
1836:                            + MessageHelper.collectionInfoString(
1837:                                    getCollectionPersisters()[0], id,
1838:                                    getFactory()));
1839:                }
1840:
1841:                Serializable[] ids = new Serializable[] { id };
1842:                try {
1843:                    doQueryAndInitializeNonLazyCollections(session,
1844:                            new QueryParameters(new Type[] { type }, ids, ids),
1845:                            true);
1846:                } catch (SQLException sqle) {
1847:                    throw JDBCExceptionHelper.convert(factory
1848:                            .getSQLExceptionConverter(), sqle,
1849:                            "could not initialize a collection: "
1850:                                    + MessageHelper.collectionInfoString(
1851:                                            getCollectionPersisters()[0], id,
1852:                                            getFactory()), getSQLString());
1853:                }
1854:
1855:                log.debug("done loading collection");
1856:
1857:            }
1858:
1859:            /**
1860:             * Called by wrappers that batch initialize collections
1861:             */
1862:            public final void loadCollectionBatch(
1863:                    final SessionImplementor session, final Serializable[] ids,
1864:                    final Type type) throws HibernateException {
1865:
1866:                if (log.isDebugEnabled()) {
1867:                    log.debug("batch loading collection: "
1868:                            + MessageHelper.collectionInfoString(
1869:                                    getCollectionPersisters()[0], ids,
1870:                                    getFactory()));
1871:                }
1872:
1873:                Type[] idTypes = new Type[ids.length];
1874:                Arrays.fill(idTypes, type);
1875:                try {
1876:                    doQueryAndInitializeNonLazyCollections(session,
1877:                            new QueryParameters(idTypes, ids, ids), true);
1878:                } catch (SQLException sqle) {
1879:                    throw JDBCExceptionHelper.convert(factory
1880:                            .getSQLExceptionConverter(), sqle,
1881:                            "could not initialize a collection batch: "
1882:                                    + MessageHelper.collectionInfoString(
1883:                                            getCollectionPersisters()[0], ids,
1884:                                            getFactory()), getSQLString());
1885:                }
1886:
1887:                log.debug("done batch load");
1888:
1889:            }
1890:
1891:            /**
1892:             * Called by subclasses that batch initialize collections
1893:             */
1894:            protected final void loadCollectionSubselect(
1895:                    final SessionImplementor session, final Serializable[] ids,
1896:                    final Object[] parameterValues,
1897:                    final Type[] parameterTypes, final Map namedParameters,
1898:                    final Type type) throws HibernateException {
1899:
1900:                Type[] idTypes = new Type[ids.length];
1901:                Arrays.fill(idTypes, type);
1902:                try {
1903:                    doQueryAndInitializeNonLazyCollections(session,
1904:                            new QueryParameters(parameterTypes,
1905:                                    parameterValues, namedParameters, ids),
1906:                            true);
1907:                } catch (SQLException sqle) {
1908:                    throw JDBCExceptionHelper.convert(factory
1909:                            .getSQLExceptionConverter(), sqle,
1910:                            "could not load collection by subselect: "
1911:                                    + MessageHelper.collectionInfoString(
1912:                                            getCollectionPersisters()[0], ids,
1913:                                            getFactory()), getSQLString());
1914:                }
1915:            }
1916:
1917:            /**
1918:             * Return the query results, using the query cache, called
1919:             * by subclasses that implement cacheable queries
1920:             */
1921:            protected List list(final SessionImplementor session,
1922:                    final QueryParameters queryParameters,
1923:                    final Set querySpaces, final Type[] resultTypes)
1924:                    throws HibernateException {
1925:
1926:                final boolean cacheable = factory.getSettings()
1927:                        .isQueryCacheEnabled()
1928:                        && queryParameters.isCacheable();
1929:
1930:                if (cacheable) {
1931:                    return listUsingQueryCache(session, queryParameters,
1932:                            querySpaces, resultTypes);
1933:                } else {
1934:                    return listIgnoreQueryCache(session, queryParameters);
1935:                }
1936:            }
1937:
1938:            private List listIgnoreQueryCache(SessionImplementor session,
1939:                    QueryParameters queryParameters) {
1940:                return getResultList(doList(session, queryParameters),
1941:                        queryParameters.getResultTransformer());
1942:            }
1943:
1944:            private List listUsingQueryCache(final SessionImplementor session,
1945:                    final QueryParameters queryParameters,
1946:                    final Set querySpaces, final Type[] resultTypes) {
1947:
1948:                QueryCache queryCache = factory.getQueryCache(queryParameters
1949:                        .getCacheRegion());
1950:
1951:                Set filterKeys = FilterKey.createFilterKeys(session
1952:                        .getEnabledFilters(), session.getEntityMode());
1953:                QueryKey key = new QueryKey(getSQLString(), queryParameters,
1954:                        filterKeys, session.getEntityMode());
1955:
1956:                List result = getResultFromQueryCache(session, queryParameters,
1957:                        querySpaces, resultTypes, queryCache, key);
1958:
1959:                if (result == null) {
1960:                    result = doList(session, queryParameters);
1961:
1962:                    putResultInQueryCache(session, queryParameters,
1963:                            resultTypes, queryCache, key, result);
1964:                }
1965:
1966:                return getResultList(result, queryParameters
1967:                        .getResultTransformer());
1968:            }
1969:
1970:            private List getResultFromQueryCache(
1971:                    final SessionImplementor session,
1972:                    final QueryParameters queryParameters,
1973:                    final Set querySpaces, final Type[] resultTypes,
1974:                    final QueryCache queryCache, final QueryKey key) {
1975:                List result = null;
1976:
1977:                if (session.getCacheMode().isGetEnabled()) {
1978:                    result = queryCache.get(key, resultTypes, queryParameters
1979:                            .isNaturalKeyLookup(), querySpaces, session);
1980:
1981:                    if (factory.getStatistics().isStatisticsEnabled()) {
1982:                        if (result == null) {
1983:                            factory.getStatisticsImplementor().queryCacheMiss(
1984:                                    getQueryIdentifier(),
1985:                                    queryCache.getRegionName());
1986:                        } else {
1987:                            factory.getStatisticsImplementor().queryCacheHit(
1988:                                    getQueryIdentifier(),
1989:                                    queryCache.getRegionName());
1990:                        }
1991:                    }
1992:
1993:                }
1994:
1995:                return result;
1996:            }
1997:
1998:            private void putResultInQueryCache(
1999:                    final SessionImplementor session,
2000:                    final QueryParameters queryParameters,
2001:                    final Type[] resultTypes, final QueryCache queryCache,
2002:                    final QueryKey key, final List result) {
2003:
2004:                if (session.getCacheMode().isPutEnabled()) {
2005:                    boolean put = queryCache.put(key, resultTypes, result,
2006:                            queryParameters.isNaturalKeyLookup(), session);
2007:                    if (put && factory.getStatistics().isStatisticsEnabled()) {
2008:                        factory.getStatisticsImplementor().queryCachePut(
2009:                                getQueryIdentifier(),
2010:                                queryCache.getRegionName());
2011:                    }
2012:                }
2013:            }
2014:
2015:            /**
2016:             * Actually execute a query, ignoring the query cache
2017:             */
2018:            protected List doList(final SessionImplementor session,
2019:                    final QueryParameters queryParameters)
2020:                    throws HibernateException {
2021:
2022:                final boolean stats = getFactory().getStatistics()
2023:                        .isStatisticsEnabled();
2024:                long startTime = 0;
2025:                if (stats)
2026:                    startTime = System.currentTimeMillis();
2027:
2028:                List result;
2029:                try {
2030:                    result = doQueryAndInitializeNonLazyCollections(session,
2031:                            queryParameters, true);
2032:                } catch (SQLException sqle) {
2033:                    throw JDBCExceptionHelper.convert(factory
2034:                            .getSQLExceptionConverter(), sqle,
2035:                            "could not execute query", getSQLString());
2036:                }
2037:
2038:                if (stats) {
2039:                    getFactory().getStatisticsImplementor().queryExecuted(
2040:                            getQueryIdentifier(), result.size(),
2041:                            System.currentTimeMillis() - startTime);
2042:                }
2043:
2044:                return result;
2045:            }
2046:
2047:            /**
2048:             * Check whether the current loader can support returning ScrollableResults.
2049:             *
2050:             * @throws HibernateException
2051:             */
2052:            protected void checkScrollability() throws HibernateException {
2053:                // Allows various loaders (ok mainly the QueryLoader :) to check
2054:                // whether scrolling of their result set should be allowed.
2055:                //
2056:                // By default it is allowed.
2057:                return;
2058:            }
2059:
2060:            /**
2061:             * Does the result set to be scrolled contain collection fetches?
2062:             *
2063:             * @return True if it does, and thus needs the special fetching scroll
2064:             * functionality; false otherwise.
2065:             */
2066:            protected boolean needsFetchingScroll() {
2067:                return false;
2068:            }
2069:
2070:            /**
2071:             * Return the query results, as an instance of <tt>ScrollableResults</tt>
2072:             *
2073:             * @param queryParameters The parameters with which the query should be executed.
2074:             * @param returnTypes The expected return types of the query
2075:             * @param holderInstantiator If the return values are expected to be wrapped
2076:             * in a holder, this is the thing that knows how to wrap them.
2077:             * @param session The session from which the scroll request originated.
2078:             * @return The ScrollableResults instance.
2079:             * @throws HibernateException Indicates an error executing the query, or constructing
2080:             * the ScrollableResults.
2081:             */
2082:            protected ScrollableResults scroll(
2083:                    final QueryParameters queryParameters,
2084:                    final Type[] returnTypes,
2085:                    final HolderInstantiator holderInstantiator,
2086:                    final SessionImplementor session) throws HibernateException {
2087:
2088:                checkScrollability();
2089:
2090:                final boolean stats = getQueryIdentifier() != null
2091:                        && getFactory().getStatistics().isStatisticsEnabled();
2092:                long startTime = 0;
2093:                if (stats)
2094:                    startTime = System.currentTimeMillis();
2095:
2096:                try {
2097:
2098:                    PreparedStatement st = prepareQueryStatement(
2099:                            queryParameters, true, session);
2100:                    ResultSet rs = getResultSet(st, queryParameters
2101:                            .hasAutoDiscoverScalarTypes(), queryParameters
2102:                            .isCallable(), queryParameters.getRowSelection(),
2103:                            session);
2104:
2105:                    if (stats) {
2106:                        getFactory().getStatisticsImplementor().queryExecuted(
2107:                                getQueryIdentifier(), 0,
2108:                                System.currentTimeMillis() - startTime);
2109:                    }
2110:
2111:                    if (needsFetchingScroll()) {
2112:                        return new FetchingScrollableResultsImpl(rs, st,
2113:                                session, this , queryParameters, returnTypes,
2114:                                holderInstantiator);
2115:                    } else {
2116:                        return new ScrollableResultsImpl(rs, st, session, this ,
2117:                                queryParameters, returnTypes,
2118:                                holderInstantiator);
2119:                    }
2120:
2121:                } catch (SQLException sqle) {
2122:                    throw JDBCExceptionHelper.convert(factory
2123:                            .getSQLExceptionConverter(), sqle,
2124:                            "could not execute query using scroll",
2125:                            getSQLString());
2126:                }
2127:
2128:            }
2129:
2130:            /**
2131:             * Calculate and cache select-clause suffixes. Must be
2132:             * called by subclasses after instantiation.
2133:             */
2134:            protected void postInstantiate() {
2135:            }
2136:
2137:            /**
2138:             * Get the result set descriptor
2139:             */
2140:            protected abstract EntityAliases[] getEntityAliases();
2141:
2142:            protected abstract CollectionAliases[] getCollectionAliases();
2143:
2144:            /**
2145:             * Identifies the query for statistics reporting, if null,
2146:             * no statistics will be reported
2147:             */
2148:            protected String getQueryIdentifier() {
2149:                return null;
2150:            }
2151:
2152:            public final SessionFactoryImplementor getFactory() {
2153:                return factory;
2154:            }
2155:
2156:            public String toString() {
2157:                return getClass().getName() + '(' + getSQLString() + ')';
2158:            }
2159:
2160:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.