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

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » orm » hibernate3 » [javadoc | source]