1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.impl;
26
27 import java.io.IOException;
28 import java.io.InvalidObjectException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.io.ObjectStreamException;
32 import java.io.Serializable;
33 import java.sql.Connection;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.Iterator;
39 import java.util.Map;
40 import java.util.Properties;
41 import java.util.Set;
42 import java.util.LinkedHashSet;
43 import javax.naming.NamingException;
44 import javax.naming.Reference;
45 import javax.naming.StringRefAddr;
46 import javax.transaction.TransactionManager;
47
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import org.hibernate.AssertionFailure;
52 import org.hibernate.ConnectionReleaseMode;
53 import org.hibernate.EntityMode;
54 import org.hibernate.HibernateException;
55 import org.hibernate.Interceptor;
56 import org.hibernate.MappingException;
57 import org.hibernate.ObjectNotFoundException;
58 import org.hibernate.QueryException;
59 import org.hibernate.SessionFactory;
60 import org.hibernate.StatelessSession;
61 import org.hibernate.SessionFactoryObserver;
62 import org.hibernate.EntityNameResolver;
63 import org.hibernate.EntityNameResolver;
64 import org.hibernate.tuple.entity.EntityTuplizer;
65 import org.hibernate.cache.CacheKey;
66 import org.hibernate.cache.CollectionRegion;
67 import org.hibernate.cache.EntityRegion;
68 import org.hibernate.cache.QueryCache;
69 import org.hibernate.cache.Region;
70 import org.hibernate.cache.UpdateTimestampsCache;
71 import org.hibernate.cache.access.AccessType;
72 import org.hibernate.cache.access.CollectionRegionAccessStrategy;
73 import org.hibernate.cache.access.EntityRegionAccessStrategy;
74 import org.hibernate.cache.impl.CacheDataDescriptionImpl;
75 import org.hibernate.cfg.Configuration;
76 import org.hibernate.cfg.Environment;
77 import org.hibernate.cfg.Settings;
78 import org.hibernate.connection.ConnectionProvider;
79 import org.hibernate.context.CurrentSessionContext;
80 import org.hibernate.context.JTASessionContext;
81 import org.hibernate.context.ManagedSessionContext;
82 import org.hibernate.context.ThreadLocalSessionContext;
83 import org.hibernate.dialect.Dialect;
84 import org.hibernate.dialect.function.SQLFunctionRegistry;
85 import org.hibernate.engine.FilterDefinition;
86 import org.hibernate.engine.Mapping;
87 import org.hibernate.engine.NamedQueryDefinition;
88 import org.hibernate.engine.NamedSQLQueryDefinition;
89 import org.hibernate.engine.ResultSetMappingDefinition;
90 import org.hibernate.engine.SessionFactoryImplementor;
91 import org.hibernate.engine.query.QueryPlanCache;
92 import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
93 import org.hibernate.event.EventListeners;
94 import org.hibernate.exception.SQLExceptionConverter;
95 import org.hibernate.id.IdentifierGenerator;
96 import org.hibernate.id.UUIDHexGenerator;
97 import org.hibernate.jdbc.BatcherFactory;
98 import org.hibernate.mapping.Collection;
99 import org.hibernate.mapping.PersistentClass;
100 import org.hibernate.mapping.RootClass;
101 import org.hibernate.metadata.ClassMetadata;
102 import org.hibernate.metadata.CollectionMetadata;
103 import org.hibernate.persister.PersisterFactory;
104 import org.hibernate.persister.collection.CollectionPersister;
105 import org.hibernate.persister.entity.EntityPersister;
106 import org.hibernate.persister.entity.Queryable;
107 import org.hibernate.pretty.MessageHelper;
108 import org.hibernate.proxy.EntityNotFoundDelegate;
109 import org.hibernate.stat.Statistics;
110 import org.hibernate.stat.StatisticsImpl;
111 import org.hibernate.stat.StatisticsImplementor;
112 import org.hibernate.tool.hbm2ddl.SchemaExport;
113 import org.hibernate.tool.hbm2ddl.SchemaUpdate;
114 import org.hibernate.tool.hbm2ddl.SchemaValidator;
115 import org.hibernate.transaction.TransactionFactory;
116 import org.hibernate.type.AssociationType;
117 import org.hibernate.type.Type;
118 import org.hibernate.util.CollectionHelper;
119 import org.hibernate.util.ReflectHelper;
120 import org.hibernate.util.EmptyIterator;
121
122
123 /**
124 * Concrete implementation of the <tt>SessionFactory</tt> interface. Has the following
125 * responsibilites
126 * <ul>
127 * <li>caches configuration settings (immutably)
128 * <li>caches "compiled" mappings ie. <tt>EntityPersister</tt>s and
129 * <tt>CollectionPersister</tt>s (immutable)
130 * <li>caches "compiled" queries (memory sensitive cache)
131 * <li>manages <tt>PreparedStatement</tt>s
132 * <li> delegates JDBC <tt>Connection</tt> management to the <tt>ConnectionProvider</tt>
133 * <li>factory for instances of <tt>SessionImpl</tt>
134 * </ul>
135 * This class must appear immutable to clients, even if it does all kinds of caching
136 * and pooling under the covers. It is crucial that the class is not only thread
137 * safe, but also highly concurrent. Synchronization must be used extremely sparingly.
138 *
139 * @see org.hibernate.connection.ConnectionProvider
140 * @see org.hibernate.classic.Session
141 * @see org.hibernate.hql.QueryTranslator
142 * @see org.hibernate.persister.entity.EntityPersister
143 * @see org.hibernate.persister.collection.CollectionPersister
144 * @author Gavin King
145 */
146 public final class SessionFactoryImpl implements SessionFactory, SessionFactoryImplementor {
147
148 private static final Logger log = LoggerFactory.getLogger(SessionFactoryImpl.class);
149 private static final IdentifierGenerator UUID_GENERATOR = new UUIDHexGenerator();
150
151 private final String name;
152 private final String uuid;
153
154 private final transient Map entityPersisters;
155 private final transient Map classMetadata;
156 private final transient Map collectionPersisters;
157 private final transient Map collectionMetadata;
158 private final transient Map collectionRolesByEntityParticipant;
159 private final transient Map identifierGenerators;
160 private final transient Map namedQueries;
161 private final transient Map namedSqlQueries;
162 private final transient Map sqlResultSetMappings;
163 private final transient Map filters;
164 private final transient Map imports;
165 private final transient Interceptor interceptor;
166 private final transient Settings settings;
167 private final transient Properties properties;
168 private transient SchemaExport schemaExport;
169 private final transient TransactionManager transactionManager;
170 private final transient QueryCache queryCache;
171 private final transient UpdateTimestampsCache updateTimestampsCache;
172 private final transient Map queryCaches;
173 private final transient Map allCacheRegions = new HashMap();
174 private final transient StatisticsImpl statistics = new StatisticsImpl(this);
175 private final transient EventListeners eventListeners;
176 private final transient CurrentSessionContext currentSessionContext;
177 private final transient EntityNotFoundDelegate entityNotFoundDelegate;
178 private final transient SQLFunctionRegistry sqlFunctionRegistry;
179 private final transient SessionFactoryObserver observer;
180 private final transient HashMap entityNameResolvers = new HashMap();
181
182 private final QueryPlanCache queryPlanCache = new QueryPlanCache( this );
183
184 private transient boolean isClosed = false;
185
186 public SessionFactoryImpl(
187 Configuration cfg,
188 Mapping mapping,
189 Settings settings,
190 EventListeners listeners,
191 SessionFactoryObserver observer) throws HibernateException {
192
193 log.info("building session factory");
194
195 this.properties = new Properties();
196 this.properties.putAll( cfg.getProperties() );
197 this.interceptor = cfg.getInterceptor();
198 this.settings = settings;
199 this.sqlFunctionRegistry = new SQLFunctionRegistry(settings.getDialect(), cfg.getSqlFunctions());
200 this.eventListeners = listeners;
201 this.observer = observer != null ? observer : new SessionFactoryObserver() {
202 public void sessionFactoryCreated(SessionFactory factory) {
203 }
204 public void sessionFactoryClosed(SessionFactory factory) {
205 }
206 };
207 this.filters = new HashMap();
208 this.filters.putAll( cfg.getFilterDefinitions() );
209
210 if ( log.isDebugEnabled() ) {
211 log.debug("Session factory constructed with filter configurations : " + filters);
212 }
213
214 if ( log.isDebugEnabled() ) {
215 log.debug(
216 "instantiating session factory with properties: " + properties
217 );
218 }
219
220 // Caches
221 settings.getRegionFactory().start( settings, properties );
222
223 //Generators:
224
225 identifierGenerators = new HashMap();
226 Iterator classes = cfg.getClassMappings();
227 while ( classes.hasNext() ) {
228 PersistentClass model = (PersistentClass) classes.next();
229 if ( !model.isInherited() ) {
230 IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(
231 settings.getDialect(),
232 settings.getDefaultCatalogName(),
233 settings.getDefaultSchemaName(),
234 (RootClass) model
235 );
236 identifierGenerators.put( model.getEntityName(), generator );
237 }
238 }
239
240
241 ///////////////////////////////////////////////////////////////////////
242 // Prepare persisters and link them up with their cache
243 // region/access-strategy
244
245 final String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + ".";
246
247 entityPersisters = new HashMap();
248 Map entityAccessStrategies = new HashMap();
249 Map classMeta = new HashMap();
250 classes = cfg.getClassMappings();
251 while ( classes.hasNext() ) {
252 final PersistentClass model = (PersistentClass) classes.next();
253 model.prepareTemporaryTables( mapping, settings.getDialect() );
254 final String cacheRegionName = cacheRegionPrefix + model.getRootClass().getCacheRegionName();
255 // cache region is defined by the root-class in the hierarchy...
256 EntityRegionAccessStrategy accessStrategy = ( EntityRegionAccessStrategy ) entityAccessStrategies.get( cacheRegionName );
257 if ( accessStrategy == null && settings.isSecondLevelCacheEnabled() ) {
258 final AccessType accessType = AccessType.parse( model.getCacheConcurrencyStrategy() );
259 if ( accessType != null ) {
260 log.trace( "Building cache for entity data [" + model.getEntityName() + "]" );
261 EntityRegion entityRegion = settings.getRegionFactory().buildEntityRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
262 accessStrategy = entityRegion.buildAccessStrategy( accessType );
263 entityAccessStrategies.put( cacheRegionName, accessStrategy );
264 allCacheRegions.put( cacheRegionName, entityRegion );
265 }
266 }
267 EntityPersister cp = PersisterFactory.createClassPersister( model, accessStrategy, this, mapping );
268 entityPersisters.put( model.getEntityName(), cp );
269 classMeta.put( model.getEntityName(), cp.getClassMetadata() );
270 }
271 classMetadata = Collections.unmodifiableMap(classMeta);
272
273 Map tmpEntityToCollectionRoleMap = new HashMap();
274 collectionPersisters = new HashMap();
275 Iterator collections = cfg.getCollectionMappings();
276 while ( collections.hasNext() ) {
277 Collection model = (Collection) collections.next();
278 final String cacheRegionName = cacheRegionPrefix + model.getCacheRegionName();
279 final AccessType accessType = AccessType.parse( model.getCacheConcurrencyStrategy() );
280 CollectionRegionAccessStrategy accessStrategy = null;
281 if ( accessType != null && settings.isSecondLevelCacheEnabled() ) {
282 log.trace( "Building cache for collection data [" + model.getRole() + "]" );
283 CollectionRegion collectionRegion = settings.getRegionFactory().buildCollectionRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
284 accessStrategy = collectionRegion.buildAccessStrategy( accessType );
285 entityAccessStrategies.put( cacheRegionName, accessStrategy );
286 allCacheRegions.put( cacheRegionName, collectionRegion );
287 }
288 CollectionPersister persister = PersisterFactory.createCollectionPersister( cfg, model, accessStrategy, this) ;
289 collectionPersisters.put( model.getRole(), persister.getCollectionMetadata() );
290 Type indexType = persister.getIndexType();
291 if ( indexType != null && indexType.isAssociationType() && !indexType.isAnyType() ) {
292 String entityName = ( ( AssociationType ) indexType ).getAssociatedEntityName( this );
293 Set roles = ( Set ) tmpEntityToCollectionRoleMap.get( entityName );
294 if ( roles == null ) {
295 roles = new HashSet();
296 tmpEntityToCollectionRoleMap.put( entityName, roles );
297 }
298 roles.add( persister.getRole() );
299 }
300 Type elementType = persister.getElementType();
301 if ( elementType.isAssociationType() && !elementType.isAnyType() ) {
302 String entityName = ( ( AssociationType ) elementType ).getAssociatedEntityName( this );
303 Set roles = ( Set ) tmpEntityToCollectionRoleMap.get( entityName );
304 if ( roles == null ) {
305 roles = new HashSet();
306 tmpEntityToCollectionRoleMap.put( entityName, roles );
307 }
308 roles.add( persister.getRole() );
309 }
310 }
311 collectionMetadata = Collections.unmodifiableMap(collectionPersisters);
312 Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator();
313 while ( itr.hasNext() ) {
314 final Map.Entry entry = ( Map.Entry ) itr.next();
315 entry.setValue( Collections.unmodifiableSet( ( Set ) entry.getValue() ) );
316 }
317 collectionRolesByEntityParticipant = Collections.unmodifiableMap( tmpEntityToCollectionRoleMap );
318
319 //Named Queries:
320 namedQueries = new HashMap( cfg.getNamedQueries() );
321 namedSqlQueries = new HashMap( cfg.getNamedSQLQueries() );
322 sqlResultSetMappings = new HashMap( cfg.getSqlResultSetMappings() );
323 imports = new HashMap( cfg.getImports() );
324
325 // after *all* persisters and named queries are registered
326 Iterator iter = entityPersisters.values().iterator();
327 while ( iter.hasNext() ) {
328 final EntityPersister persister = ( ( EntityPersister ) iter.next() );
329 persister.postInstantiate();
330 registerEntityNameResolvers( persister );
331
332 }
333 iter = collectionPersisters.values().iterator();
334 while ( iter.hasNext() ) {
335 final CollectionPersister persister = ( ( CollectionPersister ) iter.next() );
336 persister.postInstantiate();
337 }
338
339 //JNDI + Serialization:
340
341 name = settings.getSessionFactoryName();
342 try {
343 uuid = (String) UUID_GENERATOR.generate(null, null);
344 }
345 catch (Exception e) {
346 throw new AssertionFailure("Could not generate UUID");
347 }
348 SessionFactoryObjectFactory.addInstance(uuid, name, this, properties);
349
350 log.debug("instantiated session factory");
351
352 if ( settings.isAutoCreateSchema() ) {
353 new SchemaExport( cfg, settings ).create( false, true );
354 }
355 if ( settings.isAutoUpdateSchema() ) {
356 new SchemaUpdate( cfg, settings ).execute( false, true );
357 }
358 if ( settings.isAutoValidateSchema() ) {
359 new SchemaValidator( cfg, settings ).validate();
360 }
361 if ( settings.isAutoDropSchema() ) {
362 schemaExport = new SchemaExport( cfg, settings );
363 }
364
365 if ( settings.getTransactionManagerLookup()!=null ) {
366 log.debug("obtaining JTA TransactionManager");
367 transactionManager = settings.getTransactionManagerLookup().getTransactionManager(properties);
368 }
369 else {
370 if ( settings.getTransactionFactory().isTransactionManagerRequired() ) {
371 throw new HibernateException("The chosen transaction strategy requires access to the JTA TransactionManager");
372 }
373 transactionManager = null;
374 }
375
376 currentSessionContext = buildCurrentSessionContext();
377
378 if ( settings.isQueryCacheEnabled() ) {
379 updateTimestampsCache = new UpdateTimestampsCache(settings, properties);
380 queryCache = settings.getQueryCacheFactory()
381 .getQueryCache(null, updateTimestampsCache, settings, properties);
382 queryCaches = new HashMap();
383 allCacheRegions.put( updateTimestampsCache.getRegion().getName(), updateTimestampsCache.getRegion() );
384 allCacheRegions.put( queryCache.getRegion().getName(), queryCache.getRegion() );
385 }
386 else {
387 updateTimestampsCache = null;
388 queryCache = null;
389 queryCaches = null;
390 }
391
392 //checking for named queries
393 if ( settings.isNamedQueryStartupCheckingEnabled() ) {
394 Map errors = checkNamedQueries();
395 if ( !errors.isEmpty() ) {
396 Set keys = errors.keySet();
397 StringBuffer failingQueries = new StringBuffer( "Errors in named queries: " );
398 for ( Iterator iterator = keys.iterator() ; iterator.hasNext() ; ) {
399 String queryName = ( String ) iterator.next();
400 HibernateException e = ( HibernateException ) errors.get( queryName );
401 failingQueries.append( queryName );
402 if ( iterator.hasNext() ) {
403 failingQueries.append( ", " );
404 }
405 log.error( "Error in named query: " + queryName, e );
406 }
407 throw new HibernateException( failingQueries.toString() );
408 }
409 }
410
411 //stats
412 getStatistics().setStatisticsEnabled( settings.isStatisticsEnabled() );
413
414 // EntityNotFoundDelegate
415 EntityNotFoundDelegate entityNotFoundDelegate = cfg.getEntityNotFoundDelegate();
416 if ( entityNotFoundDelegate == null ) {
417 entityNotFoundDelegate = new EntityNotFoundDelegate() {
418 public void handleEntityNotFound(String entityName, Serializable id) {
419 throw new ObjectNotFoundException( id, entityName );
420 }
421 };
422 }
423 this.entityNotFoundDelegate = entityNotFoundDelegate;
424
425 this.observer.sessionFactoryCreated( this );
426 }
427
428 private void registerEntityNameResolvers(EntityPersister persister) {
429 if ( persister.getEntityMetamodel() == null || persister.getEntityMetamodel().getTuplizerMapping() == null ) {
430 return;
431 }
432 Iterator itr = persister.getEntityMetamodel().getTuplizerMapping().iterateTuplizers();
433 while ( itr.hasNext() ) {
434 final EntityTuplizer tuplizer = ( EntityTuplizer ) itr.next();
435 registerEntityNameResolvers( tuplizer );
436 }
437 }
438
439 private void registerEntityNameResolvers(EntityTuplizer tuplizer) {
440 EntityNameResolver[] resolvers = tuplizer.getEntityNameResolvers();
441 if ( resolvers == null ) {
442 return;
443 }
444
445 for ( int i = 0; i < resolvers.length; i++ ) {
446 registerEntityNameResolver( resolvers[i], tuplizer.getEntityMode() );
447 }
448 }
449
450 public void registerEntityNameResolver(EntityNameResolver resolver, EntityMode entityMode) {
451 LinkedHashSet resolversForMode = ( LinkedHashSet ) entityNameResolvers.get( entityMode );
452 if ( resolversForMode == null ) {
453 resolversForMode = new LinkedHashSet();
454 entityNameResolvers.put( entityMode, resolversForMode );
455 }
456 resolversForMode.add( resolver );
457 }
458
459 public Iterator iterateEntityNameResolvers(EntityMode entityMode) {
460 Set actualEntityNameResolvers = ( Set ) entityNameResolvers.get( entityMode );
461 return actualEntityNameResolvers == null
462 ? EmptyIterator.INSTANCE
463 : actualEntityNameResolvers.iterator();
464 }
465
466 public QueryPlanCache getQueryPlanCache() {
467 return queryPlanCache;
468 }
469
470 private Map checkNamedQueries() throws HibernateException {
471 Map errors = new HashMap();
472
473 // Check named HQL queries
474 log.debug("Checking " + namedQueries.size() + " named HQL queries");
475 Iterator itr = namedQueries.entrySet().iterator();
476 while ( itr.hasNext() ) {
477 final Map.Entry entry = ( Map.Entry ) itr.next();
478 final String queryName = ( String ) entry.getKey();
479 final NamedQueryDefinition qd = ( NamedQueryDefinition ) entry.getValue();
480 // this will throw an error if there's something wrong.
481 try {
482 log.debug("Checking named query: " + queryName);
483 //TODO: BUG! this currently fails for named queries for non-POJO entities
484 queryPlanCache.getHQLQueryPlan( qd.getQueryString(), false, CollectionHelper.EMPTY_MAP );
485 }
486 catch ( QueryException e ) {
487 errors.put( queryName, e );
488 }
489 catch ( MappingException e ) {
490 errors.put( queryName, e );
491 }
492 }
493
494 log.debug("Checking " + namedSqlQueries.size() + " named SQL queries");
495 itr = namedSqlQueries.entrySet().iterator();
496 while ( itr.hasNext() ) {
497 final Map.Entry entry = ( Map.Entry ) itr.next();
498 final String queryName = ( String ) entry.getKey();
499 final NamedSQLQueryDefinition qd = ( NamedSQLQueryDefinition ) entry.getValue();
500 // this will throw an error if there's something wrong.
501 try {
502 log.debug("Checking named SQL query: " + queryName);
503 // TODO : would be really nice to cache the spec on the query-def so as to not have to re-calc the hash;
504 // currently not doable though because of the resultset-ref stuff...
505 NativeSQLQuerySpecification spec;
506 if ( qd.getResultSetRef() != null ) {
507 ResultSetMappingDefinition definition = ( ResultSetMappingDefinition ) sqlResultSetMappings.get( qd.getResultSetRef() );
508 if ( definition == null ) {
509 throw new MappingException( "Unable to find resultset-ref definition: " + qd.getResultSetRef() );
510 }
511 spec = new NativeSQLQuerySpecification(
512 qd.getQueryString(),
513 definition.getQueryReturns(),
514 qd.getQuerySpaces()
515 );
516 }
517 else {
518 spec = new NativeSQLQuerySpecification(
519 qd.getQueryString(),
520 qd.getQueryReturns(),
521 qd.getQuerySpaces()
522 );
523 }
524 queryPlanCache.getNativeSQLQueryPlan( spec );
525 }
526 catch ( QueryException e ) {
527 errors.put( queryName, e );
528 }
529 catch ( MappingException e ) {
530 errors.put( queryName, e );
531 }
532 }
533
534 return errors;
535 }
536
537 public StatelessSession openStatelessSession() {
538 return new StatelessSessionImpl( null, this );
539 }
540
541 public StatelessSession openStatelessSession(Connection connection) {
542 return new StatelessSessionImpl( connection, this );
543 }
544
545 private SessionImpl openSession(
546 Connection connection,
547 boolean autoClose,
548 long timestamp,
549 Interceptor sessionLocalInterceptor
550 ) {
551 return new SessionImpl(
552 connection,
553 this,
554 autoClose,
555 timestamp,
556 sessionLocalInterceptor == null ? interceptor : sessionLocalInterceptor,
557 settings.getDefaultEntityMode(),
558 settings.isFlushBeforeCompletionEnabled(),
559 settings.isAutoCloseSessionEnabled(),
560 settings.getConnectionReleaseMode()
561 );
562 }
563
564 public org.hibernate.classic.Session openSession(Connection connection, Interceptor sessionLocalInterceptor) {
565 return openSession(connection, false, Long.MIN_VALUE, sessionLocalInterceptor);
566 }
567
568 public org.hibernate.classic.Session openSession(Interceptor sessionLocalInterceptor)
569 throws HibernateException {
570 // note that this timestamp is not correct if the connection provider
571 // returns an older JDBC connection that was associated with a
572 // transaction that was already begun before openSession() was called
573 // (don't know any possible solution to this!)
574 long timestamp = settings.getRegionFactory().nextTimestamp();
575 return openSession( null, true, timestamp, sessionLocalInterceptor );
576 }
577
578 public org.hibernate.classic.Session openSession(Connection connection) {
579 return openSession(connection, interceptor); //prevents this session from adding things to cache
580 }
581
582 public org.hibernate.classic.Session openSession() throws HibernateException {
583 return openSession(interceptor);
584 }
585
586 public org.hibernate.classic.Session openTemporarySession() throws HibernateException {
587 return new SessionImpl(
588 null,
589 this,
590 true,
591 settings.getRegionFactory().nextTimestamp(),
592 interceptor,
593 settings.getDefaultEntityMode(),
594 false,
595 false,
596 ConnectionReleaseMode.AFTER_STATEMENT
597 );
598 }
599
600 public org.hibernate.classic.Session openSession(
601 final Connection connection,
602 final boolean flushBeforeCompletionEnabled,
603 final boolean autoCloseSessionEnabled,
604 final ConnectionReleaseMode connectionReleaseMode) throws HibernateException {
605 return new SessionImpl(
606 connection,
607 this,
608 true,
609 settings.getRegionFactory().nextTimestamp(),
610 interceptor,
611 settings.getDefaultEntityMode(),
612 flushBeforeCompletionEnabled,
613 autoCloseSessionEnabled,
614 connectionReleaseMode
615 );
616 }
617
618 public org.hibernate.classic.Session getCurrentSession() throws HibernateException {
619 if ( currentSessionContext == null ) {
620 throw new HibernateException( "No CurrentSessionContext configured!" );
621 }
622 return currentSessionContext.currentSession();
623 }
624
625 public EntityPersister getEntityPersister(String entityName) throws MappingException {
626 EntityPersister result = (EntityPersister) entityPersisters.get(entityName);
627 if (result==null) {
628 throw new MappingException( "Unknown entity: " + entityName );
629 }
630 return result;
631 }
632
633 public CollectionPersister getCollectionPersister(String role) throws MappingException {
634 CollectionPersister result = (CollectionPersister) collectionPersisters.get(role);
635 if (result==null) {
636 throw new MappingException( "Unknown collection role: " + role );
637 }
638 return result;
639 }
640
641 public Settings getSettings() {
642 return settings;
643 }
644
645 public Dialect getDialect() {
646 return settings.getDialect();
647 }
648
649 public Interceptor getInterceptor()
650 {
651 return interceptor;
652 }
653
654 public TransactionFactory getTransactionFactory() {
655 return settings.getTransactionFactory();
656 }
657
658 public TransactionManager getTransactionManager() {
659 return transactionManager;
660 }
661
662 public SQLExceptionConverter getSQLExceptionConverter() {
663 return settings.getSQLExceptionConverter();
664 }
665
666 public Set getCollectionRolesByEntityParticipant(String entityName) {
667 return ( Set ) collectionRolesByEntityParticipant.get( entityName );
668 }
669
670 // from javax.naming.Referenceable
671 public Reference getReference() throws NamingException {
672 log.debug("Returning a Reference to the SessionFactory");
673 return new Reference(
674 SessionFactoryImpl.class.getName(),
675 new StringRefAddr("uuid", uuid),
676 SessionFactoryObjectFactory.class.getName(),
677 null
678 );
679 }
680
681 private Object readResolve() throws ObjectStreamException {
682 log.trace("Resolving serialized SessionFactory");
683 // look for the instance by uuid
684 Object result = SessionFactoryObjectFactory.getInstance(uuid);
685 if (result==null) {
686 // in case we were deserialized in a different JVM, look for an instance with the same name
687 // (alternatively we could do an actual JNDI lookup here....)
688 result = SessionFactoryObjectFactory.getNamedInstance(name);
689 if (result==null) {
690 throw new InvalidObjectException("Could not find a SessionFactory named: " + name);
691 }
692 else {
693 log.debug("resolved SessionFactory by name");
694 }
695 }
696 else {
697 log.debug("resolved SessionFactory by uid");
698 }
699 return result;
700 }
701
702 public NamedQueryDefinition getNamedQuery(String queryName) {
703 return (NamedQueryDefinition) namedQueries.get(queryName);
704 }
705
706 public NamedSQLQueryDefinition getNamedSQLQuery(String queryName) {
707 return (NamedSQLQueryDefinition) namedSqlQueries.get(queryName);
708 }
709
710 public ResultSetMappingDefinition getResultSetMapping(String resultSetName) {
711 return (ResultSetMappingDefinition) sqlResultSetMappings.get(resultSetName);
712 }
713
714 public Type getIdentifierType(String className) throws MappingException {
715 return getEntityPersister(className).getIdentifierType();
716 }
717 public String getIdentifierPropertyName(String className) throws MappingException {
718 return getEntityPersister(className).getIdentifierPropertyName();
719 }
720
721 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
722 log.trace("deserializing");
723 in.defaultReadObject();
724 log.debug("deserialized: " + uuid);
725 }
726
727 private void writeObject(ObjectOutputStream out) throws IOException {
728 log.debug("serializing: " + uuid);
729 out.defaultWriteObject();
730 log.trace("serialized");
731 }
732
733 public Type[] getReturnTypes(String queryString) throws HibernateException {
734 return queryPlanCache.getHQLQueryPlan( queryString, false, CollectionHelper.EMPTY_MAP ).getReturnMetadata().getReturnTypes();
735 }
736
737 public String[] getReturnAliases(String queryString) throws HibernateException {
738 return queryPlanCache.getHQLQueryPlan( queryString, false, CollectionHelper.EMPTY_MAP ).getReturnMetadata().getReturnAliases();
739 }
740
741 public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException {
742 return getClassMetadata( persistentClass.getName() );
743 }
744
745 public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException {
746 return (CollectionMetadata) collectionMetadata.get(roleName);
747 }
748
749 public ClassMetadata getClassMetadata(String entityName) throws HibernateException {
750 return (ClassMetadata) classMetadata.get(entityName);
751 }
752
753 /**
754 * Return the names of all persistent (mapped) classes that extend or implement the
755 * given class or interface, accounting for implicit/explicit polymorphism settings
756 * and excluding mapped subclasses/joined-subclasses of other classes in the result.
757 */
758 public String[] getImplementors(String className) throws MappingException {
759
760 final Class clazz;
761 try {
762 clazz = ReflectHelper.classForName(className);
763 }
764 catch (ClassNotFoundException cnfe) {
765 return new String[] { className }; //for a dynamic-class
766 }
767
768 ArrayList results = new ArrayList();
769 Iterator iter = entityPersisters.values().iterator();
770 while ( iter.hasNext() ) {
771 //test this entity to see if we must query it
772 EntityPersister testPersister = (EntityPersister) iter.next();
773 if ( testPersister instanceof Queryable ) {
774 Queryable testQueryable = (Queryable) testPersister;
775 String testClassName = testQueryable.getEntityName();
776 boolean isMappedClass = className.equals(testClassName);
777 if ( testQueryable.isExplicitPolymorphism() ) {
778 if ( isMappedClass ) {
779 return new String[] {className}; //NOTE EARLY EXIT
780 }
781 }
782 else {
783 if (isMappedClass) {
784 results.add(testClassName);
785 }
786 else {
787 final Class mappedClass = testQueryable.getMappedClass( EntityMode.POJO );
788 if ( mappedClass!=null && clazz.isAssignableFrom( mappedClass ) ) {
789 final boolean assignableSuperclass;
790 if ( testQueryable.isInherited() ) {
791 Class mappedSuperclass = getEntityPersister( testQueryable.getMappedSuperclass() ).getMappedClass( EntityMode.POJO);
792 assignableSuperclass = clazz.isAssignableFrom(mappedSuperclass);
793 }
794 else {
795 assignableSuperclass = false;
796 }
797 if ( !assignableSuperclass ) {
798 results.add( testClassName );
799 }
800 }
801 }
802 }
803 }
804 }
805 return (String[]) results.toArray( new String[ results.size() ] );
806 }
807
808 public String getImportedClassName(String className) {
809 String result = (String) imports.get(className);
810 if (result==null) {
811 try {
812 ReflectHelper.classForName(className);
813 return className;
814 }
815 catch (ClassNotFoundException cnfe) {
816 return null;
817 }
818 }
819 else {
820 return result;
821 }
822 }
823
824 public Map getAllClassMetadata() throws HibernateException {
825 return classMetadata;
826 }
827
828 public Map getAllCollectionMetadata() throws HibernateException {
829 return collectionMetadata;
830 }
831
832 /**
833 * Closes the session factory, releasing all held resources.
834 *
835 * <ol>
836 * <li>cleans up used cache regions and "stops" the cache provider.
837 * <li>close the JDBC connection
838 * <li>remove the JNDI binding
839 * </ol>
840 *
841 * Note: Be aware that the sessionfactory instance still can
842 * be a "heavy" object memory wise after close() has been called. Thus
843 * it is important to not keep referencing the instance to let the garbage
844 * collector release the memory.
845 */
846 public void close() throws HibernateException {
847
848 if ( isClosed ) {
849 log.trace( "already closed" );
850 return;
851 }
852
853 log.info("closing");
854
855 isClosed = true;
856
857 Iterator iter = entityPersisters.values().iterator();
858 while ( iter.hasNext() ) {
859 EntityPersister p = (EntityPersister) iter.next();
860 if ( p.hasCache() ) {
861 p.getCacheAccessStrategy().getRegion().destroy();
862 }
863 }
864
865 iter = collectionPersisters.values().iterator();
866 while ( iter.hasNext() ) {
867 CollectionPersister p = (CollectionPersister) iter.next();
868 if ( p.hasCache() ) {
869 p.getCacheAccessStrategy().getRegion().destroy();
870 }
871 }
872
873 if ( settings.isQueryCacheEnabled() ) {
874 queryCache.destroy();
875
876 iter = queryCaches.values().iterator();
877 while ( iter.hasNext() ) {
878 QueryCache cache = (QueryCache) iter.next();
879 cache.destroy();
880 }
881 updateTimestampsCache.destroy();
882 }
883
884 settings.getRegionFactory().stop();
885
886 if ( settings.isAutoDropSchema() ) {
887 schemaExport.drop( false, true );
888 }
889
890 try {
891 settings.getConnectionProvider().close();
892 }
893 finally {
894 SessionFactoryObjectFactory.removeInstance(uuid, name, properties);
895 }
896
897 observer.sessionFactoryClosed( this );
898 eventListeners.destroyListeners();
899 }
900
901 public void evictEntity(String entityName, Serializable id) throws HibernateException {
902 EntityPersister p = getEntityPersister( entityName );
903 if ( p.hasCache() ) {
904 if ( log.isDebugEnabled() ) {
905 log.debug( "evicting second-level cache: " + MessageHelper.infoString(p, id, this) );
906 }
907 CacheKey cacheKey = new CacheKey( id, p.getIdentifierType(), p.getRootEntityName(), EntityMode.POJO, this );
908 p.getCacheAccessStrategy().evict( cacheKey );
909 }
910 }
911
912 public void evictEntity(String entityName) throws HibernateException {
913 EntityPersister p = getEntityPersister( entityName );
914 if ( p.hasCache() ) {
915 if ( log.isDebugEnabled() ) {
916 log.debug( "evicting second-level cache: " + p.getEntityName() );
917 }
918 p.getCacheAccessStrategy().evictAll();
919 }
920 }
921
922 public void evict(Class persistentClass, Serializable id) throws HibernateException {
923 EntityPersister p = getEntityPersister( persistentClass.getName() );
924 if ( p.hasCache() ) {
925 if ( log.isDebugEnabled() ) {
926 log.debug( "evicting second-level cache: " + MessageHelper.infoString(p, id, this) );
927 }
928 CacheKey cacheKey = new CacheKey( id, p.getIdentifierType(), p.getRootEntityName(), EntityMode.POJO, this );
929 p.getCacheAccessStrategy().evict( cacheKey );
930 }
931 }
932
933 public void evict(Class persistentClass) throws HibernateException {
934 EntityPersister p = getEntityPersister( persistentClass.getName() );
935 if ( p.hasCache() ) {
936 if ( log.isDebugEnabled() ) {
937 log.debug( "evicting second-level cache: " + p.getEntityName() );
938 }
939 p.getCacheAccessStrategy().evictAll();
940 }
941 }
942
943 public void evictCollection(String roleName, Serializable id) throws HibernateException {
944 CollectionPersister p = getCollectionPersister( roleName );
945 if ( p.hasCache() ) {
946 if ( log.isDebugEnabled() ) {
947 log.debug( "evicting second-level cache: " + MessageHelper.collectionInfoString(p, id, this) );
948 }
949 CacheKey cacheKey = new CacheKey( id, p.getKeyType(), p.getRole(), EntityMode.POJO, this );
950 p.getCacheAccessStrategy().evict( cacheKey );
951 }
952 }
953
954 public void evictCollection(String roleName) throws HibernateException {
955 CollectionPersister p = getCollectionPersister( roleName );
956 if ( p.hasCache() ) {
957 if ( log.isDebugEnabled() ) {
958 log.debug( "evicting second-level cache: " + p.getRole() );
959 }
960 p.getCacheAccessStrategy().evictAll();
961 }
962 }
963
964 public Type getReferencedPropertyType(String className, String propertyName)
965 throws MappingException {
966 return getEntityPersister(className).getPropertyType(propertyName);
967 }
968
969 public ConnectionProvider getConnectionProvider() {
970 return settings.getConnectionProvider();
971 }
972
973 public UpdateTimestampsCache getUpdateTimestampsCache() {
974 return updateTimestampsCache;
975 }
976
977 public QueryCache getQueryCache() {
978 return queryCache;
979 }
980
981 public QueryCache getQueryCache(String regionName) throws HibernateException {
982 if ( regionName == null ) {
983 return getQueryCache();
984 }
985
986 if ( !settings.isQueryCacheEnabled() ) {
987 return null;
988 }
989
990 synchronized ( allCacheRegions ) {
991 QueryCache currentQueryCache = ( QueryCache ) queryCaches.get( regionName );
992 if ( currentQueryCache == null ) {
993 currentQueryCache = settings.getQueryCacheFactory().getQueryCache( regionName, updateTimestampsCache, settings, properties );
994 queryCaches.put( regionName, currentQueryCache );
995 allCacheRegions.put( currentQueryCache.getRegion().getName(), currentQueryCache.getRegion() );
996 }
997 return currentQueryCache;
998 }
999 }
1000
1001 public Region getSecondLevelCacheRegion(String regionName) {
1002 synchronized ( allCacheRegions ) {
1003 return ( Region ) allCacheRegions.get( regionName );
1004 }
1005 }
1006
1007 public Map getAllSecondLevelCacheRegions() {
1008 synchronized ( allCacheRegions ) {
1009 return new HashMap( allCacheRegions );
1010 }
1011 }
1012
1013 public boolean isClosed() {
1014 return isClosed;
1015 }
1016
1017 public Statistics getStatistics() {
1018 return statistics;
1019 }
1020
1021 public StatisticsImplementor getStatisticsImplementor() {
1022 return statistics;
1023 }
1024
1025 public void evictQueries() throws HibernateException {
1026 if ( settings.isQueryCacheEnabled() ) {
1027 queryCache.clear();
1028 }
1029 }
1030
1031 public void evictQueries(String cacheRegion) throws HibernateException {
1032 if (cacheRegion==null) {
1033 throw new NullPointerException("use the zero-argument form to evict the default query cache");
1034 }
1035 else {
1036 synchronized (allCacheRegions) {
1037 if ( settings.isQueryCacheEnabled() ) {
1038 QueryCache currentQueryCache = (QueryCache) queryCaches.get(cacheRegion);
1039 if ( currentQueryCache != null ) {
1040 currentQueryCache.clear();
1041 }
1042 }
1043 }
1044 }
1045 }
1046
1047 public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
1048 FilterDefinition def = ( FilterDefinition ) filters.get( filterName );
1049 if ( def == null ) {
1050 throw new HibernateException( "No such filter configured [" + filterName + "]" );
1051 }
1052 return def;
1053 }
1054
1055 public Set getDefinedFilterNames() {
1056 return filters.keySet();
1057 }
1058
1059 public BatcherFactory getBatcherFactory() {
1060 return settings.getBatcherFactory();
1061 }
1062
1063 public IdentifierGenerator getIdentifierGenerator(String rootEntityName) {
1064 return (IdentifierGenerator) identifierGenerators.get(rootEntityName);
1065 }
1066
1067 private CurrentSessionContext buildCurrentSessionContext() {
1068 String impl = properties.getProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS );
1069 // for backward-compatability
1070 if ( impl == null && transactionManager != null ) {
1071 impl = "jta";
1072 }
1073
1074 if ( impl == null ) {
1075 return null;
1076 }
1077 else if ( "jta".equals( impl ) ) {
1078 if ( settings.getTransactionFactory().areCallbacksLocalToHibernateTransactions() ) {
1079 log.warn( "JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()" );
1080 }
1081 return new JTASessionContext( this );
1082 }
1083 else if ( "thread".equals( impl ) ) {
1084 return new ThreadLocalSessionContext( this );
1085 }
1086 else if ( "managed".equals( impl ) ) {
1087 return new ManagedSessionContext( this );
1088 }
1089 else {
1090 try {
1091 Class implClass = ReflectHelper.classForName( impl );
1092 return ( CurrentSessionContext ) implClass
1093 .getConstructor( new Class[] { SessionFactoryImplementor.class } )
1094 .newInstance( new Object[] { this } );
1095 }
1096 catch( Throwable t ) {
1097 log.error( "Unable to construct current session context [" + impl + "]", t );
1098 return null;
1099 }
1100 }
1101 }
1102
1103 public EventListeners getEventListeners()
1104 {
1105 return eventListeners;
1106 }
1107
1108 public EntityNotFoundDelegate getEntityNotFoundDelegate() {
1109 return entityNotFoundDelegate;
1110 }
1111
1112 /**
1113 * Custom serialization hook used during Session serialization.
1114 *
1115 * @param oos The stream to which to write the factory
1116 * @throws IOException Indicates problems writing out the serial data stream
1117 */
1118 void serialize(ObjectOutputStream oos) throws IOException {
1119 oos.writeUTF( uuid );
1120 oos.writeBoolean( name != null );
1121 if ( name != null ) {
1122 oos.writeUTF( name );
1123 }
1124 }
1125
1126 /**
1127 * Custom deserialization hook used during Session deserialization.
1128 *
1129 * @param ois The stream from which to "read" the factory
1130 * @return The deserialized factory
1131 * @throws IOException indicates problems reading back serial data stream
1132 * @throws ClassNotFoundException indicates problems reading back serial data stream
1133 */
1134 static SessionFactoryImpl deserialize(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1135 String uuid = ois.readUTF();
1136 boolean isNamed = ois.readBoolean();
1137 String name = null;
1138 if ( isNamed ) {
1139 name = ois.readUTF();
1140 }
1141 Object result = SessionFactoryObjectFactory.getInstance( uuid );
1142 if ( result == null ) {
1143 log.trace( "could not locate session factory by uuid [" + uuid + "] during session deserialization; trying name" );
1144 if ( isNamed ) {
1145 result = SessionFactoryObjectFactory.getNamedInstance( name );
1146 }
1147 if ( result == null ) {
1148 throw new InvalidObjectException( "could not resolve session factory during session deserialization [uuid=" + uuid + ", name=" + name + "]" );
1149 }
1150 }
1151 return ( SessionFactoryImpl ) result;
1152 }
1153
1154 public SQLFunctionRegistry getSqlFunctionRegistry() {
1155 return sqlFunctionRegistry;
1156 }
1157 }