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