0001: /*
0002: * Copyright 2002-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.springframework.orm.hibernate3;
0018:
0019: import java.io.File;
0020: import java.lang.reflect.Array;
0021: import java.sql.Connection;
0022: import java.sql.SQLException;
0023: import java.sql.Statement;
0024: import java.util.Collection;
0025: import java.util.Enumeration;
0026: import java.util.Iterator;
0027: import java.util.Map;
0028: import java.util.Properties;
0029:
0030: import javax.sql.DataSource;
0031: import javax.transaction.TransactionManager;
0032:
0033: import org.hibernate.ConnectionReleaseMode;
0034: import org.hibernate.HibernateException;
0035: import org.hibernate.Interceptor;
0036: import org.hibernate.Session;
0037: import org.hibernate.SessionFactory;
0038: import org.hibernate.cfg.Configuration;
0039: import org.hibernate.cfg.Environment;
0040: import org.hibernate.cfg.Mappings;
0041: import org.hibernate.cfg.NamingStrategy;
0042: import org.hibernate.dialect.Dialect;
0043: import org.hibernate.engine.FilterDefinition;
0044: import org.hibernate.event.EventListeners;
0045: import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
0046: import org.hibernate.transaction.JTATransactionFactory;
0047:
0048: import org.springframework.beans.BeanUtils;
0049: import org.springframework.beans.factory.BeanClassLoaderAware;
0050: import org.springframework.core.io.ClassPathResource;
0051: import org.springframework.core.io.Resource;
0052: import org.springframework.dao.DataAccessException;
0053: import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
0054: import org.springframework.jdbc.support.JdbcUtils;
0055: import org.springframework.jdbc.support.lob.LobHandler;
0056: import org.springframework.util.Assert;
0057: import org.springframework.util.ClassUtils;
0058: import org.springframework.util.StringUtils;
0059:
0060: /**
0061: * {@link org.springframework.beans.factory.FactoryBean} that creates a
0062: * Hibernate {@link org.hibernate.SessionFactory}. This is the usual way to
0063: * set up a shared Hibernate SessionFactory in a Spring application context;
0064: * the SessionFactory can then be passed to Hibernate-based DAOs via
0065: * dependency injection.
0066: *
0067: * <p>Configuration settings can either be read from a Hibernate XML file,
0068: * specified as "configLocation", or completely via this class. A typical
0069: * local configuration consists of one or more "mappingResources", various
0070: * "hibernateProperties" (not strictly necessary), and a "dataSource" that the
0071: * SessionFactory should use. The latter can also be specified via Hibernate
0072: * properties, but "dataSource" supports any Spring-configured DataSource,
0073: * instead of relying on Hibernate's own connection providers.
0074: *
0075: * <p>This SessionFactory handling strategy is appropriate for most types of
0076: * applications, from Hibernate-only single database apps to ones that need
0077: * distributed transactions. Either {@link HibernateTransactionManager} or
0078: * {@link org.springframework.transaction.jta.JtaTransactionManager} can be
0079: * used for transaction demarcation, with the latter only necessary for
0080: * transactions which span multiple databases.
0081: *
0082: * <p>This factory bean will by default expose a transaction-aware SessionFactory
0083: * proxy, letting data access code work with the plain Hibernate SessionFactory
0084: * and its <code>getCurrentSession()</code> method, while still being able to
0085: * participate in current Spring-managed transactions: with any transaction
0086: * management strategy, either local or JTA / EJB CMT, and any transaction
0087: * synchronization mechanism, either Spring or JTA. Furthermore,
0088: * <code>getCurrentSession()</code> will also seamlessly work with
0089: * a request-scoped Session managed by
0090: * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter} /
0091: * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}.
0092: *
0093: * <p><b>Requires Hibernate 3.1 or later.</b> Note that this factory will use
0094: * "on_close" as default Hibernate connection release mode, unless in the
0095: * case of a "jtaTransactionManager" specified, for the reason that
0096: * this is appropriate for most Spring-based applications (in particular when
0097: * using Spring's HibernateTransactionManager). Hibernate 3.0 used "on_close"
0098: * as its own default too; however, Hibernate 3.1 changed this to "auto"
0099: * (i.e. "after_statement" or "after_transaction").
0100: *
0101: * @author Juergen Hoeller
0102: * @since 1.2
0103: * @see HibernateTemplate#setSessionFactory
0104: * @see HibernateTransactionManager#setSessionFactory
0105: * @see #setExposeTransactionAwareSessionFactory
0106: * @see #setJtaTransactionManager
0107: * @see org.hibernate.SessionFactory#getCurrentSession()
0108: * @see HibernateTransactionManager
0109: */
0110: public class LocalSessionFactoryBean extends AbstractSessionFactoryBean
0111: implements BeanClassLoaderAware {
0112:
0113: private static final ThreadLocal configTimeDataSourceHolder = new ThreadLocal();
0114:
0115: private static final ThreadLocal configTimeTransactionManagerHolder = new ThreadLocal();
0116:
0117: private static final ThreadLocal configTimeLobHandlerHolder = new ThreadLocal();
0118:
0119: /**
0120: * Return the DataSource for the currently configured Hibernate SessionFactory,
0121: * to be used by LocalDataSourceConnectionProvoder.
0122: * <p>This instance will be set before initialization of the corresponding
0123: * SessionFactory, and reset immediately afterwards. It is thus only available
0124: * during configuration.
0125: * @see #setDataSource
0126: * @see LocalDataSourceConnectionProvider
0127: */
0128: public static DataSource getConfigTimeDataSource() {
0129: return (DataSource) configTimeDataSourceHolder.get();
0130: }
0131:
0132: /**
0133: * Return the JTA TransactionManager for the currently configured Hibernate
0134: * SessionFactory, to be used by LocalTransactionManagerLookup.
0135: * <p>This instance will be set before initialization of the corresponding
0136: * SessionFactory, and reset immediately afterwards. It is thus only available
0137: * during configuration.
0138: * @see #setJtaTransactionManager
0139: * @see LocalTransactionManagerLookup
0140: */
0141: public static TransactionManager getConfigTimeTransactionManager() {
0142: return (TransactionManager) configTimeTransactionManagerHolder
0143: .get();
0144: }
0145:
0146: /**
0147: * Return the LobHandler for the currently configured Hibernate SessionFactory,
0148: * to be used by UserType implementations like ClobStringType.
0149: * <p>This instance will be set before initialization of the corresponding
0150: * SessionFactory, and reset immediately afterwards. It is thus only available
0151: * during configuration.
0152: * @see #setLobHandler
0153: * @see org.springframework.orm.hibernate3.support.ClobStringType
0154: * @see org.springframework.orm.hibernate3.support.BlobByteArrayType
0155: * @see org.springframework.orm.hibernate3.support.BlobSerializableType
0156: */
0157: public static LobHandler getConfigTimeLobHandler() {
0158: return (LobHandler) configTimeLobHandlerHolder.get();
0159: }
0160:
0161: private Class configurationClass = Configuration.class;
0162:
0163: private Resource[] configLocations;
0164:
0165: private String[] mappingResources;
0166:
0167: private Resource[] mappingLocations;
0168:
0169: private Resource[] cacheableMappingLocations;
0170:
0171: private Resource[] mappingJarLocations;
0172:
0173: private Resource[] mappingDirectoryLocations;
0174:
0175: private Properties hibernateProperties;
0176:
0177: private TransactionManager jtaTransactionManager;
0178:
0179: private LobHandler lobHandler;
0180:
0181: private Interceptor entityInterceptor;
0182:
0183: private NamingStrategy namingStrategy;
0184:
0185: private TypeDefinitionBean[] typeDefinitions;
0186:
0187: private FilterDefinition[] filterDefinitions;
0188:
0189: private Properties entityCacheStrategies;
0190:
0191: private Properties collectionCacheStrategies;
0192:
0193: private Map eventListeners;
0194:
0195: private boolean schemaUpdate = false;
0196:
0197: private ClassLoader beanClassLoader = ClassUtils
0198: .getDefaultClassLoader();
0199:
0200: private Configuration configuration;
0201:
0202: /**
0203: * Specify the Hibernate Configuration class to use.
0204: * Default is "org.hibernate.cfg.Configuration"; any subclass of
0205: * this default Hibernate Configuration class can be specified.
0206: * <p>Can be set to "org.hibernate.cfg.AnnotationConfiguration" for
0207: * using Hibernate3 annotation support (initially only available as
0208: * alpha download separate from the main Hibernate3 distribution).
0209: * <p>Annotated packages and annotated classes can be specified via the
0210: * corresponding tags in "hibernate.cfg.xml" then, so this will usually
0211: * be combined with a "configLocation" property that points at such a
0212: * standard Hibernate configuration file.
0213: * @see #setConfigLocation
0214: * @see org.hibernate.cfg.Configuration
0215: * @see org.hibernate.cfg.AnnotationConfiguration
0216: */
0217: public void setConfigurationClass(Class configurationClass) {
0218: if (configurationClass == null
0219: || !Configuration.class
0220: .isAssignableFrom(configurationClass)) {
0221: throw new IllegalArgumentException(
0222: "configurationClass must be assignable to [org.hibernate.cfg.Configuration]");
0223: }
0224: this .configurationClass = configurationClass;
0225: }
0226:
0227: /**
0228: * Set the location of a single Hibernate XML config file, for example as
0229: * classpath resource "classpath:hibernate.cfg.xml".
0230: * <p>Note: Can be omitted when all necessary properties and mapping
0231: * resources are specified locally via this bean.
0232: * @see org.hibernate.cfg.Configuration#configure(java.net.URL)
0233: */
0234: public void setConfigLocation(Resource configLocation) {
0235: this .configLocations = new Resource[] { configLocation };
0236: }
0237:
0238: /**
0239: * Set the locations of multiple Hibernate XML config files, for example as
0240: * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml".
0241: * <p>Note: Can be omitted when all necessary properties and mapping
0242: * resources are specified locally via this bean.
0243: * @see org.hibernate.cfg.Configuration#configure(java.net.URL)
0244: */
0245: public void setConfigLocations(Resource[] configLocations) {
0246: this .configLocations = configLocations;
0247: }
0248:
0249: /**
0250: * Set Hibernate mapping resources to be found in the class path,
0251: * like "example.hbm.xml" or "mypackage/example.hbm.xml".
0252: * Analogous to mapping entries in a Hibernate XML config file.
0253: * Alternative to the more generic setMappingLocations method.
0254: * <p>Can be used to add to mappings from a Hibernate XML config file,
0255: * or to specify all mappings locally.
0256: * @see #setMappingLocations
0257: * @see org.hibernate.cfg.Configuration#addResource
0258: */
0259: public void setMappingResources(String[] mappingResources) {
0260: this .mappingResources = mappingResources;
0261: }
0262:
0263: /**
0264: * Set locations of Hibernate mapping files, for example as classpath
0265: * resource "classpath:example.hbm.xml". Supports any resource location
0266: * via Spring's resource abstraction, for example relative paths like
0267: * "WEB-INF/mappings/example.hbm.xml" when running in an application context.
0268: * <p>Can be used to add to mappings from a Hibernate XML config file,
0269: * or to specify all mappings locally.
0270: * @see org.hibernate.cfg.Configuration#addInputStream
0271: */
0272: public void setMappingLocations(Resource[] mappingLocations) {
0273: this .mappingLocations = mappingLocations;
0274: }
0275:
0276: /**
0277: * Set locations of cacheable Hibernate mapping files, for example as web app
0278: * resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location
0279: * via Spring's resource abstraction, as long as the resource can be resolved
0280: * in the file system.
0281: * <p>Can be used to add to mappings from a Hibernate XML config file,
0282: * or to specify all mappings locally.
0283: * @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File)
0284: */
0285: public void setCacheableMappingLocations(
0286: Resource[] cacheableMappingLocations) {
0287: this .cacheableMappingLocations = cacheableMappingLocations;
0288: }
0289:
0290: /**
0291: * Set locations of jar files that contain Hibernate mapping resources,
0292: * like "WEB-INF/lib/example.hbm.jar".
0293: * <p>Can be used to add to mappings from a Hibernate XML config file,
0294: * or to specify all mappings locally.
0295: * @see org.hibernate.cfg.Configuration#addJar(java.io.File)
0296: */
0297: public void setMappingJarLocations(Resource[] mappingJarLocations) {
0298: this .mappingJarLocations = mappingJarLocations;
0299: }
0300:
0301: /**
0302: * Set locations of directories that contain Hibernate mapping resources,
0303: * like "WEB-INF/mappings".
0304: * <p>Can be used to add to mappings from a Hibernate XML config file,
0305: * or to specify all mappings locally.
0306: * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File)
0307: */
0308: public void setMappingDirectoryLocations(
0309: Resource[] mappingDirectoryLocations) {
0310: this .mappingDirectoryLocations = mappingDirectoryLocations;
0311: }
0312:
0313: /**
0314: * Set Hibernate properties, such as "hibernate.dialect".
0315: * <p>Can be used to override values in a Hibernate XML config file,
0316: * or to specify all necessary properties locally.
0317: * <p>Note: Do not specify a transaction provider here when using
0318: * Spring-driven transactions. It is also advisable to omit connection
0319: * provider settings and use a Spring-set DataSource instead.
0320: * @see #setDataSource
0321: */
0322: public void setHibernateProperties(Properties hibernateProperties) {
0323: this .hibernateProperties = hibernateProperties;
0324: }
0325:
0326: /**
0327: * Return the Hibernate properties, if any. Mainly available for
0328: * configuration through property paths that specify individual keys.
0329: */
0330: public Properties getHibernateProperties() {
0331: if (this .hibernateProperties == null) {
0332: this .hibernateProperties = new Properties();
0333: }
0334: return this .hibernateProperties;
0335: }
0336:
0337: /**
0338: * Set the JTA TransactionManager to be used for Hibernate's
0339: * TransactionManagerLookup. If set, this will override corresponding
0340: * settings in Hibernate properties. Allows to use a Spring-managed
0341: * JTA TransactionManager for Hibernate's cache synchronization.
0342: * <p>Note: If this is set, the Hibernate settings should not define a
0343: * transaction manager lookup to avoid meaningless double configuration.
0344: * @see LocalTransactionManagerLookup
0345: */
0346: public void setJtaTransactionManager(
0347: TransactionManager jtaTransactionManager) {
0348: this .jtaTransactionManager = jtaTransactionManager;
0349: }
0350:
0351: /**
0352: * Set the LobHandler to be used by the SessionFactory.
0353: * Will be exposed at config time for UserType implementations.
0354: * @see #getConfigTimeLobHandler
0355: * @see org.hibernate.usertype.UserType
0356: * @see org.springframework.orm.hibernate3.support.ClobStringType
0357: * @see org.springframework.orm.hibernate3.support.BlobByteArrayType
0358: * @see org.springframework.orm.hibernate3.support.BlobSerializableType
0359: */
0360: public void setLobHandler(LobHandler lobHandler) {
0361: this .lobHandler = lobHandler;
0362: }
0363:
0364: /**
0365: * Set a Hibernate entity interceptor that allows to inspect and change
0366: * property values before writing to and reading from the database.
0367: * Will get applied to any new Session created by this factory.
0368: * <p>Such an interceptor can either be set at the SessionFactory level, i.e. on
0369: * LocalSessionFactoryBean, or at the Session level, i.e. on HibernateTemplate,
0370: * HibernateInterceptor, and HibernateTransactionManager. It's preferable to set
0371: * it on LocalSessionFactoryBean or HibernateTransactionManager to avoid repeated
0372: * configuration and guarantee consistent behavior in transactions.
0373: * @see HibernateTemplate#setEntityInterceptor
0374: * @see HibernateInterceptor#setEntityInterceptor
0375: * @see HibernateTransactionManager#setEntityInterceptor
0376: * @see org.hibernate.cfg.Configuration#setInterceptor
0377: */
0378: public void setEntityInterceptor(Interceptor entityInterceptor) {
0379: this .entityInterceptor = entityInterceptor;
0380: }
0381:
0382: /**
0383: * Set a Hibernate NamingStrategy for the SessionFactory, determining the
0384: * physical column and table names given the info in the mapping document.
0385: * @see org.hibernate.cfg.Configuration#setNamingStrategy
0386: */
0387: public void setNamingStrategy(NamingStrategy namingStrategy) {
0388: this .namingStrategy = namingStrategy;
0389: }
0390:
0391: /**
0392: * Specify the Hibernate type definitions to register with the SessionFactory,
0393: * as Spring TypeDefinitionBean instances. This is an alternative to specifying
0394: * <<typedef> elements in Hibernate mapping files.
0395: * <p>Unfortunately, Hibernate itself does not define a complete object that
0396: * represents a type definition, hence the need for Spring's TypeDefinitionBean.
0397: * @see TypeDefinitionBean
0398: * @see org.hibernate.cfg.Mappings#addTypeDef(String, String, java.util.Properties)
0399: */
0400: public void setTypeDefinitions(TypeDefinitionBean[] typeDefinitions) {
0401: this .typeDefinitions = typeDefinitions;
0402: }
0403:
0404: /**
0405: * Specify the Hibernate FilterDefinitions to register with the SessionFactory.
0406: * This is an alternative to specifying <<filter-def> elements in
0407: * Hibernate mapping files.
0408: * <p>Typically, the passed-in FilterDefinition objects will have been defined
0409: * as Spring FilterDefinitionFactoryBeans, probably as inner beans within the
0410: * LocalSessionFactoryBean definition.
0411: * @see FilterDefinitionFactoryBean
0412: * @see org.hibernate.cfg.Configuration#addFilterDefinition
0413: */
0414: public void setFilterDefinitions(
0415: FilterDefinition[] filterDefinitions) {
0416: this .filterDefinitions = filterDefinitions;
0417: }
0418:
0419: /**
0420: * Specify the cache strategies for entities (persistent classes or named entities).
0421: * This configuration setting corresponds to the <class-cache> entry
0422: * in the "hibernate.cfg.xml" configuration format.
0423: * <p>For example:
0424: * <pre>
0425: * <property name="entityCacheStrategies">
0426: * <props>
0427: * <prop key="com.mycompany.Customer">read-write</prop>
0428: * <prop key="com.mycompany.Product">read-only,myRegion</prop>
0429: * </props>
0430: * </property></pre>
0431: * Note that appending a cache region name (with a comma separator) is only
0432: * supported on Hibernate 3.1, where this functionality is publically available.
0433: * @param entityCacheStrategies properties that define entity cache strategies,
0434: * with class names as keys and cache concurrency strategies as values
0435: * @see org.hibernate.cfg.Configuration#setCacheConcurrencyStrategy(String, String)
0436: */
0437: public void setEntityCacheStrategies(
0438: Properties entityCacheStrategies) {
0439: this .entityCacheStrategies = entityCacheStrategies;
0440: }
0441:
0442: /**
0443: * Specify the cache strategies for persistent collections (with specific roles).
0444: * This configuration setting corresponds to the <collection-cache> entry
0445: * in the "hibernate.cfg.xml" configuration format.
0446: * <p>For example:
0447: * <pre>
0448: * <property name="collectionCacheStrategies">
0449: * <props>
0450: * <prop key="com.mycompany.Order.items">read-write</prop>
0451: * <prop key="com.mycompany.Product.categories">read-only,myRegion</prop>
0452: * </props>
0453: * </property></pre>
0454: * Note that appending a cache region name (with a comma separator) is only
0455: * supported on Hibernate 3.1, where this functionality is publically available.
0456: * @param collectionCacheStrategies properties that define collection cache strategies,
0457: * with collection roles as keys and cache concurrency strategies as values
0458: * @see org.hibernate.cfg.Configuration#setCollectionCacheConcurrencyStrategy(String, String)
0459: */
0460: public void setCollectionCacheStrategies(
0461: Properties collectionCacheStrategies) {
0462: this .collectionCacheStrategies = collectionCacheStrategies;
0463: }
0464:
0465: /**
0466: * Specify the Hibernate event listeners to register, with listener types
0467: * as keys and listener objects as values.
0468: * <p>Instead of a single listener object, you can also pass in a list
0469: * or set of listeners objects as value. However, this is only supported
0470: * on Hibernate 3.1.
0471: * <p>See the Hibernate documentation for further details on listener types
0472: * and associated listener interfaces.
0473: * @param eventListeners Map with listener type Strings as keys and
0474: * listener objects as values
0475: * @see org.hibernate.cfg.Configuration#setListener(String, Object)
0476: */
0477: public void setEventListeners(Map eventListeners) {
0478: this .eventListeners = eventListeners;
0479: }
0480:
0481: /**
0482: * Set whether to execute a schema update after SessionFactory initialization.
0483: * <p>For details on how to make schema update scripts work, see the Hibernate
0484: * documentation, as this class leverages the same schema update script support
0485: * in org.hibernate.cfg.Configuration as Hibernate's own SchemaUpdate tool.
0486: * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript
0487: * @see org.hibernate.tool.hbm2ddl.SchemaUpdate
0488: */
0489: public void setSchemaUpdate(boolean schemaUpdate) {
0490: this .schemaUpdate = schemaUpdate;
0491: }
0492:
0493: public void setBeanClassLoader(ClassLoader beanClassLoader) {
0494: this .beanClassLoader = beanClassLoader;
0495: }
0496:
0497: protected SessionFactory buildSessionFactory() throws Exception {
0498: // Create Configuration instance.
0499: Configuration config = newConfiguration();
0500:
0501: DataSource dataSource = getDataSource();
0502: if (dataSource != null) {
0503: // Make given DataSource available for SessionFactory configuration.
0504: configTimeDataSourceHolder.set(dataSource);
0505: }
0506: if (this .jtaTransactionManager != null) {
0507: // Make Spring-provided JTA TransactionManager available.
0508: configTimeTransactionManagerHolder
0509: .set(this .jtaTransactionManager);
0510: }
0511: if (this .lobHandler != null) {
0512: // Make given LobHandler available for SessionFactory configuration.
0513: // Do early because because mapping resource might refer to custom types.
0514: configTimeLobHandlerHolder.set(this .lobHandler);
0515: }
0516:
0517: // Analogous to Hibernate EntityManager's Ejb3Configuration:
0518: // Hibernate doesn't allow setting the bean ClassLoader explicitly,
0519: // so we need to expose it as thread context ClassLoader accordingly.
0520: Thread currentThread = Thread.currentThread();
0521: ClassLoader threadContextClassLoader = currentThread
0522: .getContextClassLoader();
0523: boolean overrideClassLoader = (this .beanClassLoader != null && !this .beanClassLoader
0524: .equals(threadContextClassLoader));
0525: if (overrideClassLoader) {
0526: currentThread.setContextClassLoader(this .beanClassLoader);
0527: }
0528:
0529: try {
0530: if (this .jtaTransactionManager != null) {
0531: // Set Spring-provided JTA TransactionManager as Hibernate property.
0532: config.setProperty(
0533: Environment.TRANSACTION_MANAGER_STRATEGY,
0534: LocalTransactionManagerLookup.class.getName());
0535: config.setProperty(Environment.TRANSACTION_STRATEGY,
0536: JTATransactionFactory.class.getName());
0537: } else {
0538: // Set connection release mode "on_close" as default.
0539: // This was the case for Hibernate 3.0; Hibernate 3.1 changed
0540: // it to "auto" (i.e. "after_statement" or "after_transaction").
0541: // However, for Spring's resource management (in particular for
0542: // HibernateTransactionManager), "on_close" is the better default.
0543: config.setProperty(Environment.RELEASE_CONNECTIONS,
0544: ConnectionReleaseMode.ON_CLOSE.toString());
0545: }
0546:
0547: if (isExposeTransactionAwareSessionFactory()) {
0548: // Set Hibernate 3.1 CurrentSessionContext implementation,
0549: // providing the Spring-managed Session as current Session.
0550: // Can be overridden by a custom value for the corresponding Hibernate property.
0551: config.setProperty(
0552: Environment.CURRENT_SESSION_CONTEXT_CLASS,
0553: SpringSessionContext.class.getName());
0554: }
0555:
0556: if (this .entityInterceptor != null) {
0557: // Set given entity interceptor at SessionFactory level.
0558: config.setInterceptor(this .entityInterceptor);
0559: }
0560:
0561: if (this .namingStrategy != null) {
0562: // Pass given naming strategy to Hibernate Configuration.
0563: config.setNamingStrategy(this .namingStrategy);
0564: }
0565:
0566: if (this .typeDefinitions != null) {
0567: // Register specified Hibernate type definitions.
0568: Mappings mappings = config.createMappings();
0569: for (int i = 0; i < this .typeDefinitions.length; i++) {
0570: TypeDefinitionBean typeDef = this .typeDefinitions[i];
0571: mappings.addTypeDef(typeDef.getTypeName(), typeDef
0572: .getTypeClass(), typeDef.getParameters());
0573: }
0574: }
0575:
0576: if (this .filterDefinitions != null) {
0577: // Register specified Hibernate FilterDefinitions.
0578: for (int i = 0; i < this .filterDefinitions.length; i++) {
0579: config
0580: .addFilterDefinition(this .filterDefinitions[i]);
0581: }
0582: }
0583:
0584: if (this .configLocations != null) {
0585: for (int i = 0; i < this .configLocations.length; i++) {
0586: // Load Hibernate configuration from given location.
0587: config.configure(this .configLocations[i].getURL());
0588: }
0589: }
0590:
0591: if (this .hibernateProperties != null) {
0592: // Add given Hibernate properties to Configuration.
0593: config.addProperties(this .hibernateProperties);
0594: }
0595:
0596: if (dataSource != null) {
0597: boolean actuallyTransactionAware = (isUseTransactionAwareDataSource() || dataSource instanceof TransactionAwareDataSourceProxy);
0598: // Set Spring-provided DataSource as Hibernate ConnectionProvider.
0599: config
0600: .setProperty(
0601: Environment.CONNECTION_PROVIDER,
0602: actuallyTransactionAware ? TransactionAwareDataSourceConnectionProvider.class
0603: .getName()
0604: : LocalDataSourceConnectionProvider.class
0605: .getName());
0606: }
0607:
0608: if (this .mappingResources != null) {
0609: // Register given Hibernate mapping definitions, contained in resource files.
0610: for (int i = 0; i < this .mappingResources.length; i++) {
0611: Resource resource = new ClassPathResource(
0612: this .mappingResources[i].trim(),
0613: this .beanClassLoader);
0614: config.addInputStream(resource.getInputStream());
0615: }
0616: }
0617:
0618: if (this .mappingLocations != null) {
0619: // Register given Hibernate mapping definitions, contained in resource files.
0620: for (int i = 0; i < this .mappingLocations.length; i++) {
0621: config.addInputStream(this .mappingLocations[i]
0622: .getInputStream());
0623: }
0624: }
0625:
0626: if (this .cacheableMappingLocations != null) {
0627: // Register given cacheable Hibernate mapping definitions, read from the file system.
0628: for (int i = 0; i < this .cacheableMappingLocations.length; i++) {
0629: config
0630: .addCacheableFile(this .cacheableMappingLocations[i]
0631: .getFile());
0632: }
0633: }
0634:
0635: if (this .mappingJarLocations != null) {
0636: // Register given Hibernate mapping definitions, contained in jar files.
0637: for (int i = 0; i < this .mappingJarLocations.length; i++) {
0638: Resource resource = this .mappingJarLocations[i];
0639: config.addJar(resource.getFile());
0640: }
0641: }
0642:
0643: if (this .mappingDirectoryLocations != null) {
0644: // Register all Hibernate mapping definitions in the given directories.
0645: for (int i = 0; i < this .mappingDirectoryLocations.length; i++) {
0646: File file = this .mappingDirectoryLocations[i]
0647: .getFile();
0648: if (!file.isDirectory()) {
0649: throw new IllegalArgumentException(
0650: "Mapping directory location ["
0651: + this .mappingDirectoryLocations[i]
0652: + "] does not denote a directory");
0653: }
0654: config.addDirectory(file);
0655: }
0656: }
0657:
0658: if (this .entityCacheStrategies != null) {
0659: // Register cache strategies for mapped entities.
0660: for (Enumeration classNames = this .entityCacheStrategies
0661: .propertyNames(); classNames.hasMoreElements();) {
0662: String className = (String) classNames
0663: .nextElement();
0664: String[] strategyAndRegion = StringUtils
0665: .commaDelimitedListToStringArray(this .entityCacheStrategies
0666: .getProperty(className));
0667: if (strategyAndRegion.length > 1) {
0668: config.setCacheConcurrencyStrategy(className,
0669: strategyAndRegion[0],
0670: strategyAndRegion[1]);
0671: } else if (strategyAndRegion.length > 0) {
0672: config.setCacheConcurrencyStrategy(className,
0673: strategyAndRegion[0]);
0674: }
0675: }
0676: }
0677:
0678: if (this .collectionCacheStrategies != null) {
0679: // Register cache strategies for mapped collections.
0680: for (Enumeration collRoles = this .collectionCacheStrategies
0681: .propertyNames(); collRoles.hasMoreElements();) {
0682: String collRole = (String) collRoles.nextElement();
0683: String[] strategyAndRegion = StringUtils
0684: .commaDelimitedListToStringArray(this .collectionCacheStrategies
0685: .getProperty(collRole));
0686: if (strategyAndRegion.length > 1) {
0687: config.setCollectionCacheConcurrencyStrategy(
0688: collRole, strategyAndRegion[0],
0689: strategyAndRegion[1]);
0690: } else if (strategyAndRegion.length > 0) {
0691: config.setCollectionCacheConcurrencyStrategy(
0692: collRole, strategyAndRegion[0]);
0693: }
0694: }
0695: }
0696:
0697: if (this .eventListeners != null) {
0698: // Register specified Hibernate event listeners.
0699: for (Iterator it = this .eventListeners.entrySet()
0700: .iterator(); it.hasNext();) {
0701: Map.Entry entry = (Map.Entry) it.next();
0702: Assert
0703: .isTrue(entry.getKey() instanceof String,
0704: "Event listener key needs to be of type String");
0705: String listenerType = (String) entry.getKey();
0706: Object listenerObject = entry.getValue();
0707: if (listenerObject instanceof Collection) {
0708: Collection listeners = (Collection) listenerObject;
0709: EventListeners listenerRegistry = config
0710: .getEventListeners();
0711: Object[] listenerArray = (Object[]) Array
0712: .newInstance(
0713: listenerRegistry
0714: .getListenerClassFor(listenerType),
0715: listeners.size());
0716: listenerArray = listeners
0717: .toArray(listenerArray);
0718: config
0719: .setListeners(listenerType,
0720: listenerArray);
0721: } else {
0722: config
0723: .setListener(listenerType,
0724: listenerObject);
0725: }
0726: }
0727: }
0728:
0729: // Perform custom post-processing in subclasses.
0730: postProcessConfiguration(config);
0731:
0732: // Build SessionFactory instance.
0733: logger.info("Building new Hibernate SessionFactory");
0734: this .configuration = config;
0735: return newSessionFactory(config);
0736: }
0737:
0738: finally {
0739: if (dataSource != null) {
0740: // Reset DataSource holder.
0741: configTimeDataSourceHolder.set(null);
0742: }
0743: if (this .jtaTransactionManager != null) {
0744: // Reset TransactionManager holder.
0745: configTimeTransactionManagerHolder.set(null);
0746: }
0747: if (this .lobHandler != null) {
0748: // Reset LobHandler holder.
0749: configTimeLobHandlerHolder.set(null);
0750: }
0751: if (overrideClassLoader) {
0752: // Reset original thread context ClassLoader.
0753: currentThread
0754: .setContextClassLoader(threadContextClassLoader);
0755: }
0756: }
0757: }
0758:
0759: /**
0760: * Subclasses can override this method to perform custom initialization
0761: * of the Configuration instance used for SessionFactory creation.
0762: * The properties of this LocalSessionFactoryBean will be applied to
0763: * the Configuration object that gets returned here.
0764: * <p>The default implementation creates a new Configuration instance.
0765: * A custom implementation could prepare the instance in a specific way,
0766: * or use a custom Configuration subclass.
0767: * @return the Configuration instance
0768: * @throws HibernateException in case of Hibernate initialization errors
0769: * @see org.hibernate.cfg.Configuration#Configuration()
0770: */
0771: protected Configuration newConfiguration()
0772: throws HibernateException {
0773: return (Configuration) BeanUtils
0774: .instantiateClass(this .configurationClass);
0775: }
0776:
0777: /**
0778: * To be implemented by subclasses that want to to perform custom
0779: * post-processing of the Configuration object after this FactoryBean
0780: * performed its default initialization.
0781: * @param config the current Configuration object
0782: * @throws HibernateException in case of Hibernate initialization errors
0783: */
0784: protected void postProcessConfiguration(Configuration config)
0785: throws HibernateException {
0786: }
0787:
0788: /**
0789: * Subclasses can override this method to perform custom initialization
0790: * of the SessionFactory instance, creating it via the given Configuration
0791: * object that got prepared by this LocalSessionFactoryBean.
0792: * <p>The default implementation invokes Configuration's buildSessionFactory.
0793: * A custom implementation could prepare the instance in a specific way,
0794: * or use a custom SessionFactoryImpl subclass.
0795: * @param config Configuration prepared by this LocalSessionFactoryBean
0796: * @return the SessionFactory instance
0797: * @throws HibernateException in case of Hibernate initialization errors
0798: * @see org.hibernate.cfg.Configuration#buildSessionFactory
0799: */
0800: protected SessionFactory newSessionFactory(Configuration config)
0801: throws HibernateException {
0802: return config.buildSessionFactory();
0803: }
0804:
0805: /**
0806: * Return the Configuration object used to build the SessionFactory.
0807: * Allows access to configuration metadata stored there (rarely needed).
0808: * @throws IllegalStateException if the Configuration object has not been initialized yet
0809: */
0810: public final Configuration getConfiguration() {
0811: if (this .configuration == null) {
0812: throw new IllegalStateException(
0813: "Configuration not initialized yet");
0814: }
0815: return this .configuration;
0816: }
0817:
0818: /**
0819: * Executes schema update if requested.
0820: * @see #setSchemaUpdate
0821: * @see #updateDatabaseSchema()
0822: */
0823: protected void afterSessionFactoryCreation() throws Exception {
0824: if (this .schemaUpdate) {
0825: DataSource dataSource = getDataSource();
0826: if (dataSource != null) {
0827: // Make given DataSource available for the schema update,
0828: // which unfortunately reinstantiates a ConnectionProvider.
0829: configTimeDataSourceHolder.set(dataSource);
0830: }
0831: try {
0832: updateDatabaseSchema();
0833: } finally {
0834: if (dataSource != null) {
0835: // Reset DataSource holder.
0836: configTimeDataSourceHolder.set(null);
0837: }
0838: }
0839: }
0840: }
0841:
0842: /**
0843: * Allows for schema export on shutdown.
0844: */
0845: public void destroy() throws HibernateException {
0846: DataSource dataSource = getDataSource();
0847: if (dataSource != null) {
0848: // Make given DataSource available for potential SchemaExport,
0849: // which unfortunately reinstantiates a ConnectionProvider.
0850: configTimeDataSourceHolder.set(dataSource);
0851: }
0852: try {
0853: super .destroy();
0854: } finally {
0855: if (dataSource != null) {
0856: // Reset DataSource holder.
0857: configTimeDataSourceHolder.set(null);
0858: }
0859: }
0860: }
0861:
0862: /**
0863: * Execute schema drop script, determined by the Configuration object
0864: * used for creating the SessionFactory. A replacement for Hibernate's
0865: * SchemaExport class, to be invoked on application setup.
0866: * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
0867: * SessionFactory to be able to invoke this method, e.g. via
0868: * <code>LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");</code>.
0869: * <p>Uses the SessionFactory that this bean generates for accessing a JDBC
0870: * connection to perform the script.
0871: * @throws org.springframework.dao.DataAccessException in case of script execution errors
0872: * @see org.hibernate.cfg.Configuration#generateDropSchemaScript
0873: * @see org.hibernate.tool.hbm2ddl.SchemaExport#drop
0874: */
0875: public void dropDatabaseSchema() throws DataAccessException {
0876: logger
0877: .info("Dropping database schema for Hibernate SessionFactory");
0878: HibernateTemplate hibernateTemplate = new HibernateTemplate(
0879: getSessionFactory());
0880: hibernateTemplate.execute(new HibernateCallback() {
0881: public Object doInHibernate(Session session)
0882: throws HibernateException, SQLException {
0883: Connection con = session.connection();
0884: Dialect dialect = Dialect.getDialect(getConfiguration()
0885: .getProperties());
0886: String[] sql = getConfiguration()
0887: .generateDropSchemaScript(dialect);
0888: executeSchemaScript(con, sql);
0889: return null;
0890: }
0891: });
0892: }
0893:
0894: /**
0895: * Execute schema creation script, determined by the Configuration object
0896: * used for creating the SessionFactory. A replacement for Hibernate's
0897: * SchemaExport class, to be invoked on application setup.
0898: * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
0899: * SessionFactory to be able to invoke this method, e.g. via
0900: * <code>LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");</code>.
0901: * <p>Uses the SessionFactory that this bean generates for accessing a JDBC
0902: * connection to perform the script.
0903: * @throws DataAccessException in case of script execution errors
0904: * @see org.hibernate.cfg.Configuration#generateSchemaCreationScript
0905: * @see org.hibernate.tool.hbm2ddl.SchemaExport#create
0906: */
0907: public void createDatabaseSchema() throws DataAccessException {
0908: logger
0909: .info("Creating database schema for Hibernate SessionFactory");
0910: HibernateTemplate hibernateTemplate = new HibernateTemplate(
0911: getSessionFactory());
0912: hibernateTemplate.execute(new HibernateCallback() {
0913: public Object doInHibernate(Session session)
0914: throws HibernateException, SQLException {
0915: Connection con = session.connection();
0916: Dialect dialect = Dialect.getDialect(getConfiguration()
0917: .getProperties());
0918: String[] sql = getConfiguration()
0919: .generateSchemaCreationScript(dialect);
0920: executeSchemaScript(con, sql);
0921: return null;
0922: }
0923: });
0924: }
0925:
0926: /**
0927: * Execute schema update script, determined by the Configuration object
0928: * used for creating the SessionFactory. A replacement for Hibernate's
0929: * SchemaUpdate class, for automatically executing schema update scripts
0930: * on application startup. Can also be invoked manually.
0931: * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
0932: * SessionFactory to be able to invoke this method, e.g. via
0933: * <code>LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");</code>.
0934: * <p>Uses the SessionFactory that this bean generates for accessing a JDBC
0935: * connection to perform the script.
0936: * @throws DataAccessException in case of script execution errors
0937: * @see #setSchemaUpdate
0938: * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript
0939: * @see org.hibernate.tool.hbm2ddl.SchemaUpdate
0940: */
0941: public void updateDatabaseSchema() throws DataAccessException {
0942: logger
0943: .info("Updating database schema for Hibernate SessionFactory");
0944: HibernateTemplate hibernateTemplate = new HibernateTemplate(
0945: getSessionFactory());
0946: hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER);
0947: hibernateTemplate.execute(new HibernateCallback() {
0948: public Object doInHibernate(Session session)
0949: throws HibernateException, SQLException {
0950: Connection con = session.connection();
0951: Dialect dialect = Dialect.getDialect(getConfiguration()
0952: .getProperties());
0953: DatabaseMetadata metadata = new DatabaseMetadata(con,
0954: dialect);
0955: String[] sql = getConfiguration()
0956: .generateSchemaUpdateScript(dialect, metadata);
0957: executeSchemaScript(con, sql);
0958: return null;
0959: }
0960: });
0961: }
0962:
0963: /**
0964: * Execute the given schema script on the given JDBC Connection.
0965: * <p>Note that the default implementation will log unsuccessful statements
0966: * and continue to execute. Override the <code>executeSchemaStatement</code>
0967: * method to treat failures differently.
0968: * @param con the JDBC Connection to execute the script on
0969: * @param sql the SQL statements to execute
0970: * @throws SQLException if thrown by JDBC methods
0971: * @see #executeSchemaStatement
0972: */
0973: protected void executeSchemaScript(Connection con, String[] sql)
0974: throws SQLException {
0975: if (sql != null && sql.length > 0) {
0976: boolean oldAutoCommit = con.getAutoCommit();
0977: if (!oldAutoCommit) {
0978: con.setAutoCommit(true);
0979: }
0980: try {
0981: Statement stmt = con.createStatement();
0982: try {
0983: for (int i = 0; i < sql.length; i++) {
0984: executeSchemaStatement(stmt, sql[i]);
0985: }
0986: } finally {
0987: JdbcUtils.closeStatement(stmt);
0988: }
0989: } finally {
0990: if (!oldAutoCommit) {
0991: con.setAutoCommit(false);
0992: }
0993: }
0994: }
0995: }
0996:
0997: /**
0998: * Execute the given schema SQL on the given JDBC Statement.
0999: * <p>Note that the default implementation will log unsuccessful statements
1000: * and continue to execute. Override this method to treat failures differently.
1001: * @param stmt the JDBC Statement to execute the SQL on
1002: * @param sql the SQL statement to execute
1003: * @throws SQLException if thrown by JDBC methods (and considered fatal)
1004: */
1005: protected void executeSchemaStatement(Statement stmt, String sql)
1006: throws SQLException {
1007: if (logger.isDebugEnabled()) {
1008: logger.debug("Executing schema statement: " + sql);
1009: }
1010: try {
1011: stmt.executeUpdate(sql);
1012: } catch (SQLException ex) {
1013: if (logger.isWarnEnabled()) {
1014: logger
1015: .warn("Unsuccessful schema statement: " + sql,
1016: ex);
1017: }
1018: }
1019: }
1020:
1021: }
|