001/*
002 * Copyright 2002-2014 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.orm.hibernate3;
018
019import java.io.File;
020import java.lang.reflect.Array;
021import java.sql.Connection;
022import java.sql.SQLException;
023import java.sql.Statement;
024import java.util.Collection;
025import java.util.Enumeration;
026import java.util.Map;
027import java.util.Properties;
028import javax.sql.DataSource;
029import javax.transaction.TransactionManager;
030
031import org.hibernate.HibernateException;
032import org.hibernate.Interceptor;
033import org.hibernate.Session;
034import org.hibernate.SessionFactory;
035import org.hibernate.cache.RegionFactory;
036import org.hibernate.cfg.Configuration;
037import org.hibernate.cfg.Environment;
038import org.hibernate.cfg.Mappings;
039import org.hibernate.cfg.NamingStrategy;
040import org.hibernate.dialect.Dialect;
041import org.hibernate.engine.FilterDefinition;
042import org.hibernate.engine.SessionFactoryImplementor;
043import org.hibernate.event.EventListeners;
044import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
045import org.hibernate.transaction.JTATransactionFactory;
046
047import org.springframework.beans.BeanUtils;
048import org.springframework.beans.factory.BeanClassLoaderAware;
049import org.springframework.core.io.ClassPathResource;
050import org.springframework.core.io.Resource;
051import org.springframework.dao.DataAccessException;
052import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
053import org.springframework.jdbc.support.JdbcUtils;
054import org.springframework.jdbc.support.lob.LobHandler;
055import org.springframework.util.ClassUtils;
056import org.springframework.util.StringUtils;
057
058/**
059 * {@link org.springframework.beans.factory.FactoryBean} that creates a
060 * Hibernate {@link org.hibernate.SessionFactory}. This is the usual way to
061 * set up a shared Hibernate SessionFactory in a Spring application context;
062 * the SessionFactory can then be passed to Hibernate-based DAOs via
063 * dependency injection.
064 *
065 * <p>Configuration settings can either be read from a Hibernate XML file,
066 * specified as "configLocation", or completely via this class. A typical
067 * local configuration consists of one or more "mappingResources", various
068 * "hibernateProperties" (not strictly necessary), and a "dataSource" that the
069 * SessionFactory should use. The latter can also be specified via Hibernate
070 * properties, but "dataSource" supports any Spring-configured DataSource,
071 * instead of relying on Hibernate's own connection providers.
072 *
073 * <p>This SessionFactory handling strategy is appropriate for most types of
074 * applications, from Hibernate-only single database apps to ones that need
075 * distributed transactions. Either {@link HibernateTransactionManager} or
076 * {@link org.springframework.transaction.jta.JtaTransactionManager} can be
077 * used for transaction demarcation, with the latter only necessary for
078 * transactions which span multiple databases.
079 *
080 * <p>This factory bean will by default expose a transaction-aware SessionFactory
081 * proxy, letting data access code work with the plain Hibernate SessionFactory
082 * and its {@code getCurrentSession()} method, while still being able to
083 * participate in current Spring-managed transactions: with any transaction
084 * management strategy, either local or JTA / EJB CMT, and any transaction
085 * synchronization mechanism, either Spring or JTA. Furthermore,
086 * {@code getCurrentSession()} will also seamlessly work with
087 * a request-scoped Session managed by
088 * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter} /
089 * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}.
090 *
091 * <p>Requires Hibernate 3.6.x, as of Spring 4.0.
092 * Note that this factory will use "on_close" as default Hibernate connection
093 * release mode, unless in the case of a "jtaTransactionManager" specified,
094 * for the reason that this is appropriate for most Spring-based applications
095 * (in particular when using Spring's HibernateTransactionManager).
096 *
097 * @author Juergen Hoeller
098 * @since 1.2
099 * @see HibernateTemplate#setSessionFactory
100 * @see HibernateTransactionManager#setSessionFactory
101 * @see #setExposeTransactionAwareSessionFactory
102 * @see #setJtaTransactionManager
103 * @see org.hibernate.SessionFactory#getCurrentSession()
104 * @see HibernateTransactionManager
105 * @deprecated as of Spring 4.3, in favor of Hibernate 4.x/5.x
106 */
107@Deprecated
108public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware {
109
110        private static final ThreadLocal<DataSource> configTimeDataSourceHolder =
111                        new ThreadLocal<DataSource>();
112
113        private static final ThreadLocal<TransactionManager> configTimeTransactionManagerHolder =
114                        new ThreadLocal<TransactionManager>();
115
116        private static final ThreadLocal<Object> configTimeRegionFactoryHolder =
117                        new ThreadLocal<Object>();
118
119        private static final ThreadLocal<LobHandler> configTimeLobHandlerHolder =
120                        new ThreadLocal<LobHandler>();
121
122        /**
123         * Return the DataSource for the currently configured Hibernate SessionFactory,
124         * to be used by LocalDataSourceConnectionProvoder.
125         * <p>This instance will be set before initialization of the corresponding
126         * SessionFactory, and reset immediately afterwards. It is thus only available
127         * during configuration.
128         * @see #setDataSource
129         * @see LocalDataSourceConnectionProvider
130         */
131        public static DataSource getConfigTimeDataSource() {
132                return configTimeDataSourceHolder.get();
133        }
134
135        /**
136         * Return the JTA TransactionManager for the currently configured Hibernate
137         * SessionFactory, to be used by LocalTransactionManagerLookup.
138         * <p>This instance will be set before initialization of the corresponding
139         * SessionFactory, and reset immediately afterwards. It is thus only available
140         * during configuration.
141         * @see #setJtaTransactionManager
142         * @see LocalTransactionManagerLookup
143         */
144        public static TransactionManager getConfigTimeTransactionManager() {
145                return configTimeTransactionManagerHolder.get();
146        }
147
148        /**
149         * Return the RegionFactory for the currently configured Hibernate SessionFactory,
150         * to be used by LocalRegionFactoryProxy.
151         * <p>This instance will be set before initialization of the corresponding
152         * SessionFactory, and reset immediately afterwards. It is thus only available
153         * during configuration.
154         * @see #setCacheRegionFactory
155         */
156        static Object getConfigTimeRegionFactory() {
157                return configTimeRegionFactoryHolder.get();
158        }
159
160        /**
161         * Return the LobHandler for the currently configured Hibernate SessionFactory,
162         * to be used by UserType implementations like ClobStringType.
163         * <p>This instance will be set before initialization of the corresponding
164         * SessionFactory, and reset immediately afterwards. It is thus only available
165         * during configuration.
166         * @see #setLobHandler
167         * @see org.springframework.orm.hibernate3.support.ClobStringType
168         * @see org.springframework.orm.hibernate3.support.BlobByteArrayType
169         * @see org.springframework.orm.hibernate3.support.BlobSerializableType
170         */
171        public static LobHandler getConfigTimeLobHandler() {
172                return configTimeLobHandlerHolder.get();
173        }
174
175
176        private Class<? extends Configuration> configurationClass = Configuration.class;
177
178        private Resource[] configLocations;
179
180        private String[] mappingResources;
181
182        private Resource[] mappingLocations;
183
184        private Resource[] cacheableMappingLocations;
185
186        private Resource[] mappingJarLocations;
187
188        private Resource[] mappingDirectoryLocations;
189
190        private Properties hibernateProperties;
191
192        private TransactionManager jtaTransactionManager;
193
194        private RegionFactory cacheRegionFactory;
195
196        private LobHandler lobHandler;
197
198        private Interceptor entityInterceptor;
199
200        private NamingStrategy namingStrategy;
201
202        private TypeDefinitionBean[] typeDefinitions;
203
204        private FilterDefinition[] filterDefinitions;
205
206        private Properties entityCacheStrategies;
207
208        private Properties collectionCacheStrategies;
209
210        private Map<String, Object> eventListeners;
211
212        private boolean schemaUpdate = false;
213
214        private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
215
216        private Configuration configuration;
217
218
219        /**
220         * Specify the Hibernate Configuration class to use.
221         * <p>Default is {@link org.hibernate.cfg.Configuration}; any subclass
222         * of this default Hibernate Configuration class can be specified.
223         */
224        @SuppressWarnings("unchecked")
225        public void setConfigurationClass(Class<?> configurationClass) {
226                if (configurationClass == null || !Configuration.class.isAssignableFrom(configurationClass)) {
227                        throw new IllegalArgumentException(
228                                        "'configurationClass' must be assignable to [org.hibernate.cfg.Configuration]");
229                }
230                this.configurationClass = (Class<? extends Configuration>) configurationClass;
231        }
232
233        /**
234         * Set the location of a single Hibernate XML config file, for example as
235         * classpath resource "classpath:hibernate.cfg.xml".
236         * <p>Note: Can be omitted when all necessary properties and mapping
237         * resources are specified locally via this bean.
238         * @see org.hibernate.cfg.Configuration#configure(java.net.URL)
239         */
240        public void setConfigLocation(Resource configLocation) {
241                this.configLocations = new Resource[] {configLocation};
242        }
243
244        /**
245         * Set the locations of multiple Hibernate XML config files, for example as
246         * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml".
247         * <p>Note: Can be omitted when all necessary properties and mapping
248         * resources are specified locally via this bean.
249         * @see org.hibernate.cfg.Configuration#configure(java.net.URL)
250         */
251        public void setConfigLocations(Resource... configLocations) {
252                this.configLocations = configLocations;
253        }
254
255        /**
256         * Set Hibernate mapping resources to be found in the class path,
257         * like "example.hbm.xml" or "mypackage/example.hbm.xml".
258         * Analogous to mapping entries in a Hibernate XML config file.
259         * Alternative to the more generic setMappingLocations method.
260         * <p>Can be used to add to mappings from a Hibernate XML config file,
261         * or to specify all mappings locally.
262         * @see #setMappingLocations
263         * @see org.hibernate.cfg.Configuration#addResource
264         */
265        public void setMappingResources(String... mappingResources) {
266                this.mappingResources = mappingResources;
267        }
268
269        /**
270         * Set locations of Hibernate mapping files, for example as classpath
271         * resource "classpath:example.hbm.xml". Supports any resource location
272         * via Spring's resource abstraction, for example relative paths like
273         * "WEB-INF/mappings/example.hbm.xml" when running in an application context.
274         * <p>Can be used to add to mappings from a Hibernate XML config file,
275         * or to specify all mappings locally.
276         * @see org.hibernate.cfg.Configuration#addInputStream
277         */
278        public void setMappingLocations(Resource... mappingLocations) {
279                this.mappingLocations = mappingLocations;
280        }
281
282        /**
283         * Set locations of cacheable Hibernate mapping files, for example as web app
284         * resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location
285         * via Spring's resource abstraction, as long as the resource can be resolved
286         * in the file system.
287         * <p>Can be used to add to mappings from a Hibernate XML config file,
288         * or to specify all mappings locally.
289         * @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File)
290         */
291        public void setCacheableMappingLocations(Resource... cacheableMappingLocations) {
292                this.cacheableMappingLocations = cacheableMappingLocations;
293        }
294
295        /**
296         * Set locations of jar files that contain Hibernate mapping resources,
297         * like "WEB-INF/lib/example.hbm.jar".
298         * <p>Can be used to add to mappings from a Hibernate XML config file,
299         * or to specify all mappings locally.
300         * @see org.hibernate.cfg.Configuration#addJar(java.io.File)
301         */
302        public void setMappingJarLocations(Resource... mappingJarLocations) {
303                this.mappingJarLocations = mappingJarLocations;
304        }
305
306        /**
307         * Set locations of directories that contain Hibernate mapping resources,
308         * like "WEB-INF/mappings".
309         * <p>Can be used to add to mappings from a Hibernate XML config file,
310         * or to specify all mappings locally.
311         * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File)
312         */
313        public void setMappingDirectoryLocations(Resource... mappingDirectoryLocations) {
314                this.mappingDirectoryLocations = mappingDirectoryLocations;
315        }
316
317        /**
318         * Set Hibernate properties, such as "hibernate.dialect".
319         * <p>Can be used to override values in a Hibernate XML config file,
320         * or to specify all necessary properties locally.
321         * <p>Note: Do not specify a transaction provider here when using
322         * Spring-driven transactions. It is also advisable to omit connection
323         * provider settings and use a Spring-set DataSource instead.
324         * @see #setDataSource
325         */
326        public void setHibernateProperties(Properties hibernateProperties) {
327                this.hibernateProperties = hibernateProperties;
328        }
329
330        /**
331         * Return the Hibernate properties, if any. Mainly available for
332         * configuration through property paths that specify individual keys.
333         */
334        public Properties getHibernateProperties() {
335                if (this.hibernateProperties == null) {
336                        this.hibernateProperties = new Properties();
337                }
338                return this.hibernateProperties;
339        }
340
341        /**
342         * Set the JTA TransactionManager to be used for Hibernate's
343         * TransactionManagerLookup. Allows for using a Spring-managed
344         * JTA TransactionManager for Hibernate's cache synchronization.
345         * <p>Note: If this is set, the Hibernate settings should not define a
346         * transaction manager lookup to avoid meaningless double configuration.
347         * @see LocalTransactionManagerLookup
348         */
349        public void setJtaTransactionManager(TransactionManager jtaTransactionManager) {
350                this.jtaTransactionManager = jtaTransactionManager;
351        }
352
353        /**
354         * Set the Hibernate RegionFactory to use for the SessionFactory.
355         * Allows for using a Spring-managed RegionFactory instance.
356         * <p>Note: If this is set, the Hibernate settings should not define a
357         * cache provider to avoid meaningless double configuration.
358         * @see org.hibernate.cache.RegionFactory
359         */
360        public void setCacheRegionFactory(RegionFactory cacheRegionFactory) {
361                this.cacheRegionFactory = cacheRegionFactory;
362        }
363
364        /**
365         * Set the LobHandler to be used by the SessionFactory.
366         * Will be exposed at config time for UserType implementations.
367         * @see #getConfigTimeLobHandler
368         * @see org.hibernate.usertype.UserType
369         * @see org.springframework.orm.hibernate3.support.ClobStringType
370         * @see org.springframework.orm.hibernate3.support.BlobByteArrayType
371         * @see org.springframework.orm.hibernate3.support.BlobSerializableType
372         */
373        public void setLobHandler(LobHandler lobHandler) {
374                this.lobHandler = lobHandler;
375        }
376
377        /**
378         * Set a Hibernate entity interceptor that allows to inspect and change
379         * property values before writing to and reading from the database.
380         * Will get applied to any new Session created by this factory.
381         * <p>Such an interceptor can either be set at the SessionFactory level, i.e. on
382         * LocalSessionFactoryBean, or at the Session level, i.e. on HibernateTemplate,
383         * HibernateInterceptor, and HibernateTransactionManager. It's preferable to set
384         * it on LocalSessionFactoryBean or HibernateTransactionManager to avoid repeated
385         * configuration and guarantee consistent behavior in transactions.
386         * @see HibernateTemplate#setEntityInterceptor
387         * @see HibernateInterceptor#setEntityInterceptor
388         * @see HibernateTransactionManager#setEntityInterceptor
389         * @see org.hibernate.cfg.Configuration#setInterceptor
390         */
391        public void setEntityInterceptor(Interceptor entityInterceptor) {
392                this.entityInterceptor = entityInterceptor;
393        }
394
395        /**
396         * Set a Hibernate NamingStrategy for the SessionFactory, determining the
397         * physical column and table names given the info in the mapping document.
398         * @see org.hibernate.cfg.Configuration#setNamingStrategy
399         */
400        public void setNamingStrategy(NamingStrategy namingStrategy) {
401                this.namingStrategy = namingStrategy;
402        }
403
404        /**
405         * Specify the Hibernate type definitions to register with the SessionFactory,
406         * as Spring TypeDefinitionBean instances. This is an alternative to specifying
407         * <&lt;typedef&gt; elements in Hibernate mapping files.
408         * <p>Unfortunately, Hibernate itself does not define a complete object that
409         * represents a type definition, hence the need for Spring's TypeDefinitionBean.
410         * @see TypeDefinitionBean
411         * @see org.hibernate.cfg.Mappings#addTypeDef(String, String, java.util.Properties)
412         */
413        public void setTypeDefinitions(TypeDefinitionBean... typeDefinitions) {
414                this.typeDefinitions = typeDefinitions;
415        }
416
417        /**
418         * Specify the Hibernate FilterDefinitions to register with the SessionFactory.
419         * This is an alternative to specifying <&lt;filter-def&gt; elements in
420         * Hibernate mapping files.
421         * <p>Typically, the passed-in FilterDefinition objects will have been defined
422         * as Spring FilterDefinitionFactoryBeans, probably as inner beans within the
423         * LocalSessionFactoryBean definition.
424         * @see FilterDefinitionFactoryBean
425         * @see org.hibernate.cfg.Configuration#addFilterDefinition
426         */
427        public void setFilterDefinitions(FilterDefinition... filterDefinitions) {
428                this.filterDefinitions = filterDefinitions;
429        }
430
431        /**
432         * Specify the cache strategies for entities (persistent classes or named entities).
433         * This configuration setting corresponds to the &lt;class-cache&gt; entry
434         * in the "hibernate.cfg.xml" configuration format.
435         * <p>For example:
436         * <pre class="code">
437         * &lt;property name="entityCacheStrategies"&gt;
438         *   &lt;props&gt;
439         *     &lt;prop key="com.mycompany.Customer"&gt;read-write&lt;/prop&gt;
440         *     &lt;prop key="com.mycompany.Product"&gt;read-only,myRegion&lt;/prop&gt;
441         *   &lt;/props&gt;
442         * &lt;/property&gt;</pre>
443         * @param entityCacheStrategies properties that define entity cache strategies,
444         * with class names as keys and cache concurrency strategies as values
445         * @see org.hibernate.cfg.Configuration#setCacheConcurrencyStrategy(String, String)
446         */
447        public void setEntityCacheStrategies(Properties entityCacheStrategies) {
448                this.entityCacheStrategies = entityCacheStrategies;
449        }
450
451        /**
452         * Specify the cache strategies for persistent collections (with specific roles).
453         * This configuration setting corresponds to the &lt;collection-cache&gt; entry
454         * in the "hibernate.cfg.xml" configuration format.
455         * <p>For example:
456         * <pre class="code">
457         * &lt;property name="collectionCacheStrategies"&gt;
458         *   &lt;props&gt;
459         *     &lt;prop key="com.mycompany.Order.items">read-write&lt;/prop&gt;
460         *     &lt;prop key="com.mycompany.Product.categories"&gt;read-only,myRegion&lt;/prop&gt;
461         *   &lt;/props&gt;
462         * &lt;/property&gt;</pre>
463         * @param collectionCacheStrategies properties that define collection cache strategies,
464         * with collection roles as keys and cache concurrency strategies as values
465         * @see org.hibernate.cfg.Configuration#setCollectionCacheConcurrencyStrategy(String, String)
466         */
467        public void setCollectionCacheStrategies(Properties collectionCacheStrategies) {
468                this.collectionCacheStrategies = collectionCacheStrategies;
469        }
470
471        /**
472         * Specify the Hibernate event listeners to register, with listener types
473         * as keys and listener objects as values. Instead of a single listener object,
474         * you can also pass in a list or set of listeners objects as value.
475         * <p>See the Hibernate documentation for further details on listener types
476         * and associated listener interfaces.
477         * <p>See {@code org.hibernate.cfg.Configuration#setListener(String, Object)}
478         * @param eventListeners Map with listener type Strings as keys and
479         * listener objects as values
480         */
481        public void setEventListeners(Map<String, Object> eventListeners) {
482                this.eventListeners = eventListeners;
483        }
484
485        /**
486         * Set whether to execute a schema update after SessionFactory initialization.
487         * <p>For details on how to make schema update scripts work, see the Hibernate
488         * documentation, as this class leverages the same schema update script support
489         * in org.hibernate.cfg.Configuration as Hibernate's own SchemaUpdate tool.
490         * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript
491         * @see org.hibernate.tool.hbm2ddl.SchemaUpdate
492         */
493        public void setSchemaUpdate(boolean schemaUpdate) {
494                this.schemaUpdate = schemaUpdate;
495        }
496
497        @Override
498        public void setBeanClassLoader(ClassLoader beanClassLoader) {
499                this.beanClassLoader = beanClassLoader;
500        }
501
502
503        @Override
504        @SuppressWarnings("unchecked")
505        protected SessionFactory buildSessionFactory() throws Exception {
506                // Create Configuration instance.
507                Configuration config = newConfiguration();
508
509                DataSource dataSource = getDataSource();
510                if (dataSource != null) {
511                        // Make given DataSource available for SessionFactory configuration.
512                        configTimeDataSourceHolder.set(dataSource);
513                }
514                if (this.jtaTransactionManager != null) {
515                        // Make Spring-provided JTA TransactionManager available.
516                        configTimeTransactionManagerHolder.set(this.jtaTransactionManager);
517                }
518                if (this.cacheRegionFactory != null) {
519                        // Make Spring-provided Hibernate RegionFactory available.
520                        configTimeRegionFactoryHolder.set(this.cacheRegionFactory);
521                }
522                if (this.lobHandler != null) {
523                        // Make given LobHandler available for SessionFactory configuration.
524                        // Do early because mapping resource might refer to custom types.
525                        configTimeLobHandlerHolder.set(this.lobHandler);
526                }
527
528                // Analogous to Hibernate EntityManager's Ejb3Configuration:
529                // Hibernate doesn't allow setting the bean ClassLoader explicitly,
530                // so we need to expose it as thread context ClassLoader accordingly.
531                Thread currentThread = Thread.currentThread();
532                ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
533                boolean overrideClassLoader =
534                                (this.beanClassLoader != null && !this.beanClassLoader.equals(threadContextClassLoader));
535                if (overrideClassLoader) {
536                        currentThread.setContextClassLoader(this.beanClassLoader);
537                }
538
539                try {
540                        if (isExposeTransactionAwareSessionFactory()) {
541                                // Set Hibernate 3.1+ CurrentSessionContext implementation,
542                                // providing the Spring-managed Session as current Session.
543                                // Can be overridden by a custom value for the corresponding Hibernate property.
544                                config.setProperty(
545                                                Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName());
546                        }
547
548                        if (this.jtaTransactionManager != null) {
549                                // Set Spring-provided JTA TransactionManager as Hibernate property.
550                                config.setProperty(
551                                                Environment.TRANSACTION_STRATEGY, JTATransactionFactory.class.getName());
552                                config.setProperty(
553                                                Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName());
554                        }
555                        else {
556                                // Makes the Hibernate Session aware of the presence of a Spring-managed transaction.
557                                // Also sets connection release mode to ON_CLOSE by default.
558                                config.setProperty(
559                                                Environment.TRANSACTION_STRATEGY, SpringTransactionFactory.class.getName());
560                        }
561
562                        if (this.entityInterceptor != null) {
563                                // Set given entity interceptor at SessionFactory level.
564                                config.setInterceptor(this.entityInterceptor);
565                        }
566
567                        if (this.namingStrategy != null) {
568                                // Pass given naming strategy to Hibernate Configuration.
569                                config.setNamingStrategy(this.namingStrategy);
570                        }
571
572                        if (this.typeDefinitions != null) {
573                                // Register specified Hibernate type definitions.
574                                Mappings mappings = config.createMappings();
575                                for (TypeDefinitionBean typeDef : this.typeDefinitions) {
576                                        mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters());
577                                }
578                        }
579
580                        if (this.filterDefinitions != null) {
581                                // Register specified Hibernate FilterDefinitions.
582                                for (FilterDefinition filterDef : this.filterDefinitions) {
583                                        config.addFilterDefinition(filterDef);
584                                }
585                        }
586
587                        if (this.configLocations != null) {
588                                for (Resource resource : this.configLocations) {
589                                        // Load Hibernate configuration from given location.
590                                        config.configure(resource.getURL());
591                                }
592                        }
593
594                        if (this.hibernateProperties != null) {
595                                // Add given Hibernate properties to Configuration.
596                                config.addProperties(this.hibernateProperties);
597                        }
598
599                        if (dataSource != null) {
600                                Class<?> providerClass = LocalDataSourceConnectionProvider.class;
601                                if (isUseTransactionAwareDataSource() || dataSource instanceof TransactionAwareDataSourceProxy) {
602                                        providerClass = TransactionAwareDataSourceConnectionProvider.class;
603                                }
604                                else if (config.getProperty(Environment.TRANSACTION_MANAGER_STRATEGY) != null) {
605                                        providerClass = LocalJtaDataSourceConnectionProvider.class;
606                                }
607                                // Set Spring-provided DataSource as Hibernate ConnectionProvider.
608                                config.setProperty(Environment.CONNECTION_PROVIDER, providerClass.getName());
609                        }
610
611                        if (this.cacheRegionFactory != null) {
612                                // Expose Spring-provided Hibernate RegionFactory.
613                                config.setProperty(Environment.CACHE_REGION_FACTORY, LocalRegionFactoryProxy.class.getName());
614                        }
615
616                        if (this.mappingResources != null) {
617                                // Register given Hibernate mapping definitions, contained in resource files.
618                                for (String mapping : this.mappingResources) {
619                                        Resource resource = new ClassPathResource(mapping.trim(), this.beanClassLoader);
620                                        config.addInputStream(resource.getInputStream());
621                                }
622                        }
623
624                        if (this.mappingLocations != null) {
625                                // Register given Hibernate mapping definitions, contained in resource files.
626                                for (Resource resource : this.mappingLocations) {
627                                        config.addInputStream(resource.getInputStream());
628                                }
629                        }
630
631                        if (this.cacheableMappingLocations != null) {
632                                // Register given cacheable Hibernate mapping definitions, read from the file system.
633                                for (Resource resource : this.cacheableMappingLocations) {
634                                        config.addCacheableFile(resource.getFile());
635                                }
636                        }
637
638                        if (this.mappingJarLocations != null) {
639                                // Register given Hibernate mapping definitions, contained in jar files.
640                                for (Resource resource : this.mappingJarLocations) {
641                                        config.addJar(resource.getFile());
642                                }
643                        }
644
645                        if (this.mappingDirectoryLocations != null) {
646                                // Register all Hibernate mapping definitions in the given directories.
647                                for (Resource resource : this.mappingDirectoryLocations) {
648                                        File file = resource.getFile();
649                                        if (!file.isDirectory()) {
650                                                throw new IllegalArgumentException(
651                                                                "Mapping directory location [" + resource + "] does not denote a directory");
652                                        }
653                                        config.addDirectory(file);
654                                }
655                        }
656
657                        // Tell Hibernate to eagerly compile the mappings that we registered,
658                        // for availability of the mapping information in further processing.
659                        postProcessMappings(config);
660                        config.buildMappings();
661
662                        if (this.entityCacheStrategies != null) {
663                                // Register cache strategies for mapped entities.
664                                for (Enumeration<?> classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) {
665                                        String className = (String) classNames.nextElement();
666                                        String[] strategyAndRegion =
667                                                        StringUtils.commaDelimitedListToStringArray(this.entityCacheStrategies.getProperty(className));
668                                        if (strategyAndRegion.length > 1) {
669                                                config.setCacheConcurrencyStrategy(className, strategyAndRegion[0], strategyAndRegion[1]);
670                                        }
671                                        else if (strategyAndRegion.length > 0) {
672                                                config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]);
673                                        }
674                                }
675                        }
676
677                        if (this.collectionCacheStrategies != null) {
678                                // Register cache strategies for mapped collections.
679                                for (Enumeration<?> collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) {
680                                        String collRole = (String) collRoles.nextElement();
681                                        String[] strategyAndRegion =
682                                                        StringUtils.commaDelimitedListToStringArray(this.collectionCacheStrategies.getProperty(collRole));
683                                        if (strategyAndRegion.length > 1) {
684                                                config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0], strategyAndRegion[1]);
685                                        }
686                                        else if (strategyAndRegion.length > 0) {
687                                                config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]);
688                                        }
689                                }
690                        }
691
692                        if (this.eventListeners != null) {
693                                // Register specified Hibernate event listeners.
694                                for (Map.Entry<String, Object> entry : this.eventListeners.entrySet()) {
695                                        String listenerType = entry.getKey();
696                                        Object listenerObject = entry.getValue();
697                                        if (listenerObject instanceof Collection) {
698                                                Collection<Object> listeners = (Collection<Object>) listenerObject;
699                                                EventListeners listenerRegistry = config.getEventListeners();
700                                                Object[] listenerArray =
701                                                                (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size());
702                                                listenerArray = listeners.toArray(listenerArray);
703                                                config.setListeners(listenerType, listenerArray);
704                                        }
705                                        else {
706                                                config.setListener(listenerType, listenerObject);
707                                        }
708                                }
709                        }
710
711                        // Perform custom post-processing in subclasses.
712                        postProcessConfiguration(config);
713
714                        // Build SessionFactory instance.
715                        logger.info("Building new Hibernate SessionFactory");
716                        this.configuration = config;
717                        return newSessionFactory(config);
718                }
719
720                finally {
721                        if (dataSource != null) {
722                                configTimeDataSourceHolder.remove();
723                        }
724                        if (this.jtaTransactionManager != null) {
725                                configTimeTransactionManagerHolder.remove();
726                        }
727                        if (this.cacheRegionFactory != null) {
728                                configTimeRegionFactoryHolder.remove();
729                        }
730                        if (this.lobHandler != null) {
731                                configTimeLobHandlerHolder.remove();
732                        }
733                        if (overrideClassLoader) {
734                                // Reset original thread context ClassLoader.
735                                currentThread.setContextClassLoader(threadContextClassLoader);
736                        }
737                }
738        }
739
740        /**
741         * Subclasses can override this method to perform custom initialization
742         * of the Configuration instance used for SessionFactory creation.
743         * The properties of this LocalSessionFactoryBean will be applied to
744         * the Configuration object that gets returned here.
745         * <p>The default implementation creates a new Configuration instance.
746         * A custom implementation could prepare the instance in a specific way,
747         * or use a custom Configuration subclass.
748         * @return the Configuration instance
749         * @throws HibernateException in case of Hibernate initialization errors
750         * @see org.hibernate.cfg.Configuration#Configuration()
751         */
752        protected Configuration newConfiguration() throws HibernateException {
753                return BeanUtils.instantiateClass(this.configurationClass);
754        }
755
756        /**
757         * To be implemented by subclasses that want to register further mappings
758         * on the Configuration object after this FactoryBean registered its specified
759         * mappings.
760         * <p>Invoked <i>before</i> the {@code Configuration.buildMappings()} call,
761         * so that it can still extend and modify the mapping information.
762         * @param config the current Configuration object
763         * @throws HibernateException in case of Hibernate initialization errors
764         * @see org.hibernate.cfg.Configuration#buildMappings()
765         */
766        protected void postProcessMappings(Configuration config) throws HibernateException {
767        }
768
769        /**
770         * To be implemented by subclasses that want to perform custom
771         * post-processing of the Configuration object after this FactoryBean
772         * performed its default initialization.
773         * <p>Invoked <i>after</i> the {@code Configuration.buildMappings()} call,
774         * so that it can operate on the completed and fully parsed mapping information.
775         * @param config the current Configuration object
776         * @throws HibernateException in case of Hibernate initialization errors
777         * @see org.hibernate.cfg.Configuration#buildMappings()
778         */
779        protected void postProcessConfiguration(Configuration config) throws HibernateException {
780        }
781
782        /**
783         * Subclasses can override this method to perform custom initialization
784         * of the SessionFactory instance, creating it via the given Configuration
785         * object that got prepared by this LocalSessionFactoryBean.
786         * <p>The default implementation invokes Configuration's buildSessionFactory.
787         * A custom implementation could prepare the instance in a specific way,
788         * or use a custom SessionFactoryImpl subclass.
789         * @param config Configuration prepared by this LocalSessionFactoryBean
790         * @return the SessionFactory instance
791         * @throws HibernateException in case of Hibernate initialization errors
792         * @see org.hibernate.cfg.Configuration#buildSessionFactory
793         */
794        protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {
795                return config.buildSessionFactory();
796        }
797
798        /**
799         * Return the Configuration object used to build the SessionFactory.
800         * Allows for access to configuration metadata stored there (rarely needed).
801         * @throws IllegalStateException if the Configuration object has not been initialized yet
802         */
803        public final Configuration getConfiguration() {
804                if (this.configuration == null) {
805                        throw new IllegalStateException("Configuration not initialized yet");
806                }
807                return this.configuration;
808        }
809
810        /**
811         * Executes schema update if requested.
812         * @see #setSchemaUpdate
813         * @see #updateDatabaseSchema()
814         */
815        @Override
816        protected void afterSessionFactoryCreation() throws Exception {
817                if (this.schemaUpdate) {
818                        updateDatabaseSchema();
819                }
820        }
821
822        /**
823         * Allows for schema export on shutdown.
824         */
825        @Override
826        public void destroy() throws HibernateException {
827                DataSource dataSource = getDataSource();
828                if (dataSource != null) {
829                        // Make given DataSource available for potential SchemaExport,
830                        // which unfortunately reinstantiates a ConnectionProvider.
831                        configTimeDataSourceHolder.set(dataSource);
832                }
833                try {
834                        super.destroy();
835                }
836                finally {
837                        if (dataSource != null) {
838                                // Reset DataSource holder.
839                                configTimeDataSourceHolder.remove();
840                        }
841                }
842        }
843
844
845        /**
846         * Execute schema update script, determined by the Configuration object
847         * used for creating the SessionFactory. A replacement for Hibernate's
848         * SchemaUpdate class, for automatically executing schema update scripts
849         * on application startup. Can also be invoked manually.
850         * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
851         * SessionFactory to be able to invoke this method, e.g. via
852         * {@code LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");}.
853         * <p>Uses the SessionFactory that this bean generates for accessing a
854         * JDBC connection to perform the script.
855         * @throws DataAccessException in case of script execution errors
856         * @see #setSchemaUpdate
857         * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript
858         * @see org.hibernate.tool.hbm2ddl.SchemaUpdate
859         */
860        public void updateDatabaseSchema() throws DataAccessException {
861                logger.info("Updating database schema for Hibernate SessionFactory");
862                DataSource dataSource = getDataSource();
863                if (dataSource != null) {
864                        // Make given DataSource available for the schema update.
865                        configTimeDataSourceHolder.set(dataSource);
866                }
867                try {
868                        SessionFactory sessionFactory = getSessionFactory();
869                        final Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect();
870                        HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory);
871                        hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER);
872                        hibernateTemplate.execute(
873                                new HibernateCallback<Object>() {
874                                        @Override
875                                        public Object doInHibernate(Session session) throws HibernateException, SQLException {
876                                                Connection con = session.connection();
877                                                DatabaseMetadata metadata = new DatabaseMetadata(con, dialect);
878                                                String[] sql = getConfiguration().generateSchemaUpdateScript(dialect, metadata);
879                                                executeSchemaScript(con, sql);
880                                                return null;
881                                        }
882                                }
883                        );
884                }
885                finally {
886                        if (dataSource != null) {
887                                configTimeDataSourceHolder.remove();
888                        }
889                }
890        }
891
892        /**
893         * Execute schema creation script, determined by the Configuration object
894         * used for creating the SessionFactory. A replacement for Hibernate's
895         * SchemaValidator class, to be invoked after application startup.
896         * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
897         * SessionFactory to be able to invoke this method, e.g. via
898         * {@code LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");}.
899         * <p>Uses the SessionFactory that this bean generates for accessing a
900         * JDBC connection to perform the script.
901         * @throws DataAccessException in case of script execution errors
902         * @see org.hibernate.cfg.Configuration#validateSchema
903         * @see org.hibernate.tool.hbm2ddl.SchemaValidator
904         */
905        public void validateDatabaseSchema() throws DataAccessException {
906                logger.info("Validating database schema for Hibernate SessionFactory");
907                DataSource dataSource = getDataSource();
908                if (dataSource != null) {
909                        // Make given DataSource available for the schema update.
910                        configTimeDataSourceHolder.set(dataSource);
911                }
912                try {
913                        SessionFactory sessionFactory = getSessionFactory();
914                        final Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect();
915                        HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory);
916                        hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER);
917                        hibernateTemplate.execute(
918                                new HibernateCallback<Object>() {
919                                        @Override
920                                        public Object doInHibernate(Session session) throws HibernateException, SQLException {
921                                                Connection con = session.connection();
922                                                DatabaseMetadata metadata = new DatabaseMetadata(con, dialect, false);
923                                                getConfiguration().validateSchema(dialect, metadata);
924                                                return null;
925                                        }
926                                }
927                        );
928                }
929                finally {
930                        if (dataSource != null) {
931                                configTimeDataSourceHolder.remove();
932                        }
933                }
934        }
935
936        /**
937         * Execute schema drop script, determined by the Configuration object
938         * used for creating the SessionFactory. A replacement for Hibernate's
939         * SchemaExport class, to be invoked on application setup.
940         * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
941         * SessionFactory to be able to invoke this method, e.g. via
942         * {@code LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");}.
943         * <p>Uses the SessionFactory that this bean generates for accessing a
944         * JDBC connection to perform the script.
945         * @throws org.springframework.dao.DataAccessException in case of script execution errors
946         * @see org.hibernate.cfg.Configuration#generateDropSchemaScript
947         * @see org.hibernate.tool.hbm2ddl.SchemaExport#drop
948         */
949        public void dropDatabaseSchema() throws DataAccessException {
950                logger.info("Dropping database schema for Hibernate SessionFactory");
951                SessionFactory sessionFactory = getSessionFactory();
952                final Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect();
953                HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory);
954                hibernateTemplate.execute(
955                        new HibernateCallback<Object>() {
956                                @Override
957                                public Object doInHibernate(Session session) throws HibernateException, SQLException {
958                                        Connection con = session.connection();
959                                        String[] sql = getConfiguration().generateDropSchemaScript(dialect);
960                                        executeSchemaScript(con, sql);
961                                        return null;
962                                }
963                        }
964                );
965        }
966
967        /**
968         * Execute schema creation script, determined by the Configuration object
969         * used for creating the SessionFactory. A replacement for Hibernate's
970         * SchemaExport class, to be invoked on application setup.
971         * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
972         * SessionFactory to be able to invoke this method, e.g. via
973         * {@code LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");}.
974         * <p>Uses the SessionFactory that this bean generates for accessing a
975         * JDBC connection to perform the script.
976         * @throws DataAccessException in case of script execution errors
977         * @see org.hibernate.cfg.Configuration#generateSchemaCreationScript
978         * @see org.hibernate.tool.hbm2ddl.SchemaExport#create
979         */
980        public void createDatabaseSchema() throws DataAccessException {
981                logger.info("Creating database schema for Hibernate SessionFactory");
982                DataSource dataSource = getDataSource();
983                if (dataSource != null) {
984                        // Make given DataSource available for the schema update.
985                        configTimeDataSourceHolder.set(dataSource);
986                }
987                try {
988                        SessionFactory sessionFactory = getSessionFactory();
989                        final Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect();
990                        HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory);
991                        hibernateTemplate.execute(
992                                new HibernateCallback<Object>() {
993                                        @Override
994                                        public Object doInHibernate(Session session) throws HibernateException, SQLException {
995                                                Connection con = session.connection();
996                                                String[] sql = getConfiguration().generateSchemaCreationScript(dialect);
997                                                executeSchemaScript(con, sql);
998                                                return null;
999                                        }
1000                                }
1001                        );
1002                }
1003                finally {
1004                        if (dataSource != null) {
1005                                configTimeDataSourceHolder.remove();
1006                        }
1007                }
1008        }
1009
1010        /**
1011         * Execute the given schema script on the given JDBC Connection.
1012         * <p>Note that the default implementation will log unsuccessful statements
1013         * and continue to execute. Override the {@code executeSchemaStatement}
1014         * method to treat failures differently.
1015         * @param con the JDBC Connection to execute the script on
1016         * @param sql the SQL statements to execute
1017         * @throws SQLException if thrown by JDBC methods
1018         * @see #executeSchemaStatement
1019         */
1020        protected void executeSchemaScript(Connection con, String[] sql) throws SQLException {
1021                if (sql != null && sql.length > 0) {
1022                        boolean oldAutoCommit = con.getAutoCommit();
1023                        if (!oldAutoCommit) {
1024                                con.setAutoCommit(true);
1025                        }
1026                        try {
1027                                Statement stmt = con.createStatement();
1028                                try {
1029                                        for (String sqlStmt : sql) {
1030                                                executeSchemaStatement(stmt, sqlStmt);
1031                                        }
1032                                }
1033                                finally {
1034                                        JdbcUtils.closeStatement(stmt);
1035                                }
1036                        }
1037                        finally {
1038                                if (!oldAutoCommit) {
1039                                        con.setAutoCommit(false);
1040                                }
1041                        }
1042                }
1043        }
1044
1045        /**
1046         * Execute the given schema SQL on the given JDBC Statement.
1047         * <p>Note that the default implementation will log unsuccessful statements
1048         * and continue to execute. Override this method to treat failures differently.
1049         * @param stmt the JDBC Statement to execute the SQL on
1050         * @param sql the SQL statement to execute
1051         * @throws SQLException if thrown by JDBC methods (and considered fatal)
1052         */
1053        protected void executeSchemaStatement(Statement stmt, String sql) throws SQLException {
1054                if (logger.isDebugEnabled()) {
1055                        logger.debug("Executing schema statement: " + sql);
1056                }
1057                try {
1058                        stmt.executeUpdate(sql);
1059                }
1060                catch (SQLException ex) {
1061                        if (logger.isWarnEnabled()) {
1062                                logger.warn("Unsuccessful schema statement: " + sql, ex);
1063                        }
1064                }
1065        }
1066
1067}