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