From 9e8259198f07e495fd32359c0e0c197bb79a2ca0 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Thu, 31 Mar 2011 12:29:12 +0000 Subject: [PATCH] Introduce (Annotation)SessionFactoryBuilder types Large refactoring of existing *SessionFactoryBean hierarchy designed to support configuration of Hibernate SessionFactory objects within @Configuration class @Bean methods without forcing use of a FactoryBean type, which is generally discouraged due to awkwardness of programming model and lifecycle issues. Refactored and new types include: * Removal of AbstractSessionFactoryBean, replacing it with SessionFactoryBeanSupport abstract base class * Introduction of SessionFactoryBuilder and AnnotationSessionFactoryBuilder types, both direct subclasses of SessionFactoryBuilderSupport. These types are intended for direct use within @Bean methods. They expose method-chainable set* methods allowing for concise and convenient use. See JavaDoc on both types for usage examples. * LocalSessionFactoryBean and AnnotationSessionFactoryBean types are now subclasses, respectively, of the *Builder types above. LSFB and ASFB backward-compatibility has been maintained almost entirely. The one exception is that there is no longer a protected convertHibernateAccessException() method available in the hierarchy. This method was not likely often used anyway and has been replaced by the new (and public) setPersistenceExceptionTranslator() which accepts instances of type HibernateExceptionTranslator as introduced in SPR-8076. LSFB and ASFB setter method signatures have changed. They no longer return void in standard JavaBeans style but rather, and due to extending the *Builder types above, return the 'this' reference. This posed a problem because the Spring container has to date been unable to detect and provide dependency injection against non-void returning setter methods. This limitation was due to the way that the default JavaBeans Introspector class and its getBeanInfo() method works, and prompted the introduction and intergration of ExtendedBeanInfo, already completed in SPR-8079. So have no concern if you notice this signature change - it all works. Certain deprecations have been made: * All LSFB/ASFB methods related to Hibernate's CacheProvider SPI, reflecting its deprecation in Hibernate 3.3 in favor of the new RegionFactory SPI. Note these methods have been preserved only on the FactoryBean types. The new *SessionFactoryBuilder supertypes do not expose CacheProvider services at all. * All protected LSFB/ASFB methods that accept a Hibernate Configuration parameter, such as newSessionFactory(Configuration), postProcessMappings(Configuration) and postProcessConfiguration(Configuation), in favor of no-arg methods with the same names. Due to the nature of the hierarchy refactoring mentioned above, the Configuration instance is always available when these methods are called, thus no need to pass it in as a parameter. In the process, our approach to automatic detection of Hibernate dialect has been improved (it was in fact broken before). Thanks to James Roper for his suggestion in SPR-7936 as to how to fix this. See HibernateSessionFactoryConfigurationTests as a starting point for understanding the new builder-style approach to SessionFactory creation. Note especially use of the SessionFactoryBuilder#doWithConfiguration method which allows for direct programmatic configuration of the Native Hibernate (Annotation)Configuration API. As a final note, AnnotationConfiguration has been deprecated in Hibernate 3.6, and great pains have been taken to ensure that users of any supported Hibernate version (3.2 -> 3.6) will never need to (a) cast from Configuration to AnnotationConfiguration or (b) experience deprecation warnings due to being forced to use the AnnotationConfiguration API. Explore the JavaDoc around the doWithConfiguration() method and HibernateConfigurationCallback type for complete details. Issue: SPR-8066, SPR-7936, SPR-8076, SPR-8098 git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4147 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../.classpath | 2 + .../.springBeans | 3 +- .../integration-tests.iml | 12 + org.springframework.integration-tests/ivy.xml | 2 + ...rnateSessionFactoryConfigurationTests.java | 352 +++++ .../orm/hibernate3/scannable/Foo.java | 36 + ...ionSessionFactoryBeanXmlConfig-context.xml | 12 + ...calSessionFactoryBeanXmlConfig-context.xml | 18 + .../hibernate3/scannable/FooMapping.hbm.xml | 12 + .../AbstractSessionFactoryBean.java | 336 ---- .../FilterDefinitionFactoryBean.java | 37 +- .../orm/hibernate3/HibernateAccessor.java | 17 +- .../HibernateConfigurationCallback.java | 41 + .../orm/hibernate3/HibernateTemplate.java | 13 +- .../HibernateTransactionManager.java | 26 +- .../hibernate3/LocalCacheProviderProxy.java | 11 +- .../LocalDataSourceConnectionProvider.java | 15 +- .../LocalJtaDataSourceConnectionProvider.java | 4 +- .../hibernate3/LocalRegionFactoryProxy.java | 10 +- .../hibernate3/LocalSessionFactoryBean.java | 1124 +------------ .../LocalTransactionManagerLookup.java | 12 +- .../SessionFactoryBeanDelegate.java | 198 +++ .../SessionFactoryBeanOperations.java | 128 ++ .../orm/hibernate3/SessionFactoryBuilder.java | 101 ++ .../SessionFactoryBuilderSupport.java | 1408 +++++++++++++++++ .../orm/hibernate3/SessionFactoryUtils.java | 4 +- .../orm/hibernate3/SpringSessionContext.java | 7 +- ...tionAwareDataSourceConnectionProvider.java | 6 +- .../orm/hibernate3/TypeDefinitionBean.java | 25 +- .../AnnotationSessionFactoryBean.java | 278 ++-- .../AnnotationSessionFactoryBuilder.java | 300 ++++ .../hibernate3/support/AbstractLobType.java | 32 +- .../hibernate3/support/BlobByteArrayType.java | 14 +- .../support/BlobSerializableType.java | 14 +- .../hibernate3/support/BlobStringType.java | 14 +- .../hibernate3/support/ClobStringType.java | 14 +- .../IdTransferringMergeEventListener.java | 8 +- .../support/ScopedBeanInterceptor.java | 16 +- .../HibernateTransactionManagerTests.java | 15 +- .../LocalSessionFactoryBeanTests.java | 108 +- 40 files changed, 3039 insertions(+), 1746 deletions(-) create mode 100644 org.springframework.integration-tests/src/test/java/org/springframework/orm/hibernate3/HibernateSessionFactoryConfigurationTests.java create mode 100644 org.springframework.integration-tests/src/test/java/org/springframework/orm/hibernate3/scannable/Foo.java create mode 100644 org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/AnnotationSessionFactoryBeanXmlConfig-context.xml create mode 100644 org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/LocalSessionFactoryBeanXmlConfig-context.xml create mode 100644 org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/scannable/FooMapping.hbm.xml delete mode 100644 org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/AbstractSessionFactoryBean.java create mode 100644 org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateConfigurationCallback.java create mode 100644 org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBeanDelegate.java create mode 100644 org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBeanOperations.java create mode 100644 org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBuilder.java create mode 100644 org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBuilderSupport.java create mode 100644 org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBuilder.java diff --git a/org.springframework.integration-tests/.classpath b/org.springframework.integration-tests/.classpath index 26074edfbc7..d1ea9d34812 100644 --- a/org.springframework.integration-tests/.classpath +++ b/org.springframework.integration-tests/.classpath @@ -21,6 +21,7 @@ + @@ -69,6 +70,7 @@ + diff --git a/org.springframework.integration-tests/.springBeans b/org.springframework.integration-tests/.springBeans index 55e47c55ec7..5e600dfe9f4 100644 --- a/org.springframework.integration-tests/.springBeans +++ b/org.springframework.integration-tests/.springBeans @@ -1,12 +1,13 @@ 1 - + + src/test/resources/org/springframework/orm/hibernate3/LocalSessionFactoryBeanXmlConfig-context.xml diff --git a/org.springframework.integration-tests/integration-tests.iml b/org.springframework.integration-tests/integration-tests.iml index 13421323256..ea3643ecf3d 100644 --- a/org.springframework.integration-tests/integration-tests.iml +++ b/org.springframework.integration-tests/integration-tests.iml @@ -31,6 +31,7 @@ + @@ -446,6 +447,17 @@ + + + + + + + + + + + diff --git a/org.springframework.integration-tests/ivy.xml b/org.springframework.integration-tests/ivy.xml index 455fd86e83b..6b9e9730375 100644 --- a/org.springframework.integration-tests/ivy.xml +++ b/org.springframework.integration-tests/ivy.xml @@ -74,7 +74,9 @@ + + diff --git a/org.springframework.integration-tests/src/test/java/org/springframework/orm/hibernate3/HibernateSessionFactoryConfigurationTests.java b/org.springframework.integration-tests/src/test/java/org/springframework/orm/hibernate3/HibernateSessionFactoryConfigurationTests.java new file mode 100644 index 00000000000..8d72df5d4f4 --- /dev/null +++ b/org.springframework.integration-tests/src/test/java/org/springframework/orm/hibernate3/HibernateSessionFactoryConfigurationTests.java @@ -0,0 +1,352 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate3; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.util.List; + +import javax.inject.Inject; +import javax.sql.DataSource; + +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.cfg.AnnotationConfiguration; +import org.hibernate.classic.Session; +import org.junit.Ignore; +import org.junit.Test; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Feature; +import org.springframework.context.annotation.FeatureConfiguration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportResource; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; +import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBuilder; +import org.springframework.orm.hibernate3.scannable.Foo; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.config.TxAnnotationDriven; + +/** + * Integration tests for configuring Hibernate SessionFactory types + * without using a FactoryBean, e.g., within a {@link Configuration} class. + * + * @author Chris Beams + * @since 3.1 + */ +public class HibernateSessionFactoryConfigurationTests { + + @Test + public void usingLocalSessionFactoryBean() { + saveAndRetriveEntity(LocalSessionFactoryBeanXmlConfig.class); + } + + @Test + public void usingAnnotationSessionFactoryBean() { + saveAndRetriveEntity(AnnotationSessionFactoryBeanXmlConfig.class); + } + + @Ignore @Test + public void usingNativeHibernateConfiguration() { + saveAndRetriveEntity(NativeHibernateConfig.class); + } + + @Test + public void usingSessionFactoryBuilder_withConfigurationCallback() { + saveAndRetriveEntity(SessionFactoryConfig_withConfigurationCallback.class); + } + + @Test + public void usingAnnotationSessionFactoryBuilder() { + saveAndRetriveEntity(AnnotationSessionFactoryConfig.class); + } + + @Test + public void usingAnnotationSessionFactoryBuilder_withConfigurationCallback() { + saveAndRetriveEntity(AnnotationSessionFactoryConfig_withConfigurationCallback.class); + } + + @Test + public void usingAnnotationSessionFactoryBuilder_withPackagesToScan() { + saveAndRetriveEntity(AnnotationSessionFactoryConfig_withPackagesToScan.class); + } + + @Test + public void usingAnnotationSessionFactoryBuilder_withAnnotatedClasses() { + saveAndRetriveEntity(AnnotationSessionFactoryConfig_withAnnotatedClasses.class); + } + + + @Test(expected=DataAccessException.class) + public void exceptionTranslation_withLocalSessionFactoryBean() { + causeException(LocalSessionFactoryBeanXmlConfig.class, RepositoryConfig.class); + } + + @Test(expected=DataAccessException.class) + public void exceptionTranslation_withSessionFactoryBuilder() { + causeException(SessionFactoryConfig_withConfigurationCallback.class, + RepositoryConfig.class); + } + + @Test + public void usingSessionFactoryBuilder_withCustomConfigurationClass() { + saveAndRetriveEntity(SessionFactoryConfig_withCustomConfigurationClass.class); + } + + @Test(expected=IllegalStateException.class) + public void usingSessionFactoryBuilder_withLateCustomConfigurationClass() throws Throwable { + try { + saveAndRetriveEntity(SessionFactoryConfig_withLateCustomConfigurationClass.class); + } catch (BeanCreationException ex) { + Throwable cause = ex.getRootCause(); + assertThat(cause.getMessage().startsWith("setConfigurationClass() must be called before"), is(true)); + throw cause; + } + } + + + private void saveAndRetriveEntity(Class configClass) { + SessionFactory sessionFactory = new AnnotationConfigApplicationContext(configClass).getBean(SessionFactory.class); + Session session = sessionFactory.openSession(); + Transaction tx = session.beginTransaction(); + Foo foo = new Foo(); + foo.setName("f1"); + session.save(foo); + + Foo f1 = (Foo) session.createQuery("from Foo where name = 'f1'").uniqueResult(); + assertThat("No foo with name='f1' found!", f1, notNullValue()); + assertThat(f1.getName(), is("f1")); + + tx.rollback(); + } + + private void causeException(Class... configClasses) { + FooRepository fooRepository = new AnnotationConfigApplicationContext(configClasses).getBean(FooRepository.class); + fooRepository.findAll(); // will throw + } + + + @Configuration + static class DataConfig { + @Bean + DataSource dataSource() { + return new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.HSQL) + .build(); + } + } + + + @Configuration + @ImportResource("org/springframework/orm/hibernate3/LocalSessionFactoryBeanXmlConfig-context.xml") + static class LocalSessionFactoryBeanXmlConfig extends DataConfig { + } + + + interface FooRepository { + List findAll(); + } + + @Repository + public static class HibernateFooRepository implements FooRepository { + private final SessionFactory sessionFactory; + public HibernateFooRepository(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory; + } + @Transactional + @SuppressWarnings("unchecked") + public List findAll() { + return sessionFactory.getCurrentSession().createQuery("from Bogus").list(); + } + } + + + @Configuration + @Import(TxConfig.class) + static class RepositoryConfig { + @Inject SessionFactory sessionFactory; + + @Bean + FooRepository fooRepository() { + return new HibernateFooRepository(sessionFactory); + } + + @Bean + PlatformTransactionManager txManager() { + return new HibernateTransactionManager(sessionFactory); + } + + @Bean + PersistenceExceptionTranslationPostProcessor exceptionTranslationPostProcessor() { + return new PersistenceExceptionTranslationPostProcessor(); + } + + @Bean + PersistenceExceptionTranslator exceptionTranslator() { + return new HibernateExceptionTranslator(); + } + } + + + @FeatureConfiguration + static class TxConfig { + @Feature + TxAnnotationDriven tx(PlatformTransactionManager txManager) { + return new TxAnnotationDriven(txManager); + } + } + + + @Configuration + @ImportResource("org/springframework/orm/hibernate3/AnnotationSessionFactoryBeanXmlConfig-context.xml") + static class AnnotationSessionFactoryBeanXmlConfig extends DataConfig { + } + + + @Configuration + static class NativeHibernateConfig { + @Bean + SessionFactory sessionFactory() { + org.hibernate.cfg.Configuration cfg = new AnnotationConfiguration(); + return cfg.buildSessionFactory(); + } + } + + + @Configuration + static class AnnotationSessionFactoryConfig extends DataConfig { + @Bean + SessionFactory sessionFactory() throws Exception { + return new AnnotationSessionFactoryBuilder(dataSource()) + .setSchemaUpdate(true) + .doWithConfiguration(new HibernateConfigurationCallback() { + public void configure(AnnotationConfiguration configuration) { + configuration.addAnnotatedClass(Foo.class); + } + }) + .buildSessionFactory(); + } + } + + + @Configuration + static class AnnotationSessionFactoryConfig_withPackagesToScan extends DataConfig { + @Bean + SessionFactory sessionFactory() throws Exception { + return new AnnotationSessionFactoryBuilder(dataSource()) + .setSchemaUpdate(true) + .setPackagesToScan(Foo.class.getPackage().getName()) + .buildSessionFactory(); + } + } + + @Configuration + static class AnnotationSessionFactoryConfig_withAnnotatedClasses extends DataConfig { + @Bean + SessionFactory sessionFactory() throws Exception { + return new AnnotationSessionFactoryBuilder(dataSource()) + .setSchemaUpdate(true) + .setAnnotatedClasses(Foo.class, Foo.class) + .buildSessionFactory(); + } + } + + + @Configuration + static class AnnotationSessionFactoryConfig_withConfigurationCallback extends DataConfig { + @Bean + SessionFactory sessionFactory() throws Exception { + return new AnnotationSessionFactoryBuilder(dataSource()) + .setSchemaUpdate(true) + .doWithConfiguration(new HibernateConfigurationCallback() { + public void configure(AnnotationConfiguration configuration) throws Exception { + configuration.addAnnotatedClass(Foo.class); + } + }) + .buildSessionFactory(); + } + } + + + @Configuration + static class SessionFactoryConfig_withConfigurationCallback extends DataConfig { + @Bean + SessionFactory sessionFactory() throws Exception { + return new SessionFactoryBuilder(dataSource()) + .setSchemaUpdate(true) + .doWithConfiguration(new HibernateConfigurationCallback() { + public void configure(org.hibernate.cfg.Configuration configuration) throws Exception { + configuration.addFile(new File(this.getClass().getClassLoader().getResource("org/springframework/orm/hibernate3/scannable/FooMapping.hbm.xml").toURI())); + } + }) + .buildSessionFactory(); + } + } + + + @Configuration + static class SessionFactoryConfig_withCustomConfigurationClass extends DataConfig { + @Bean + SessionFactory sessionFactory() throws Exception { + SessionFactoryBuilder sfb = new SessionFactoryBuilder(dataSource()) + .setSchemaUpdate(true) + .setConfigurationClass(CustomHibernateConfiguration.class) + .doWithConfiguration(new HibernateConfigurationCallback() { + public void configure(org.hibernate.cfg.Configuration configuration) throws Exception { + configuration.addFile(new File(this.getClass().getClassLoader().getResource("org/springframework/orm/hibernate3/scannable/FooMapping.hbm.xml").toURI())); + } + }); + assertThat(sfb.getConfiguration(), instanceOf(CustomHibernateConfiguration.class)); + return sfb.buildSessionFactory(); + } + } + + + @Configuration + static class SessionFactoryConfig_withLateCustomConfigurationClass extends DataConfig { + @Bean + SessionFactory sessionFactory() throws Exception { + return new SessionFactoryBuilder(dataSource()) + .setSchemaUpdate(true) + .doWithConfiguration(new HibernateConfigurationCallback() { + public void configure(org.hibernate.cfg.Configuration configuration) throws Exception { + configuration.addFile(new File(this.getClass().getClassLoader().getResource("org/springframework/orm/hibernate3/scannable/FooMapping.hbm.xml").toURI())); + } + }) + .setConfigurationClass(CustomHibernateConfiguration.class) + .buildSessionFactory(); + } + } + + + @SuppressWarnings("serial") + static class CustomHibernateConfiguration extends org.hibernate.cfg.Configuration { + } + +} diff --git a/org.springframework.integration-tests/src/test/java/org/springframework/orm/hibernate3/scannable/Foo.java b/org.springframework.integration-tests/src/test/java/org/springframework/orm/hibernate3/scannable/Foo.java new file mode 100644 index 00000000000..0728499b09f --- /dev/null +++ b/org.springframework.integration-tests/src/test/java/org/springframework/orm/hibernate3/scannable/Foo.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate3.scannable; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Foo { + @SuppressWarnings("unused") + @Id private int id; + + private String name; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } +} \ No newline at end of file diff --git a/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/AnnotationSessionFactoryBeanXmlConfig-context.xml b/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/AnnotationSessionFactoryBeanXmlConfig-context.xml new file mode 100644 index 00000000000..dd5ec1d94c8 --- /dev/null +++ b/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/AnnotationSessionFactoryBeanXmlConfig-context.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/LocalSessionFactoryBeanXmlConfig-context.xml b/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/LocalSessionFactoryBeanXmlConfig-context.xml new file mode 100644 index 00000000000..c3a2f30b314 --- /dev/null +++ b/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/LocalSessionFactoryBeanXmlConfig-context.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + hibernate.dialect=org.hibernate.dialect.HSQLDialect + + + + + \ No newline at end of file diff --git a/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/scannable/FooMapping.hbm.xml b/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/scannable/FooMapping.hbm.xml new file mode 100644 index 00000000000..187f1452184 --- /dev/null +++ b/org.springframework.integration-tests/src/test/resources/org/springframework/orm/hibernate3/scannable/FooMapping.hbm.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/AbstractSessionFactoryBean.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/AbstractSessionFactoryBean.java deleted file mode 100644 index 135538e7953..00000000000 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/AbstractSessionFactoryBean.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2002-2010 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.orm.hibernate3; - -import javax.sql.DataSource; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.hibernate.HibernateException; -import org.hibernate.JDBCException; -import org.hibernate.SessionFactory; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.support.PersistenceExceptionTranslator; -import org.springframework.jdbc.support.SQLExceptionTranslator; - -/** - * Abstract {@link org.springframework.beans.factory.FactoryBean} that creates - * a Hibernate {@link org.hibernate.SessionFactory} within a Spring application - * context, providing general infrastructure not related to Hibernate's - * specific configuration API. - * - *

This class implements the - * {@link org.springframework.dao.support.PersistenceExceptionTranslator} - * interface, as autodetected by Spring's - * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}, - * for AOP-based translation of native exceptions to Spring DataAccessExceptions. - * Hence, the presence of e.g. LocalSessionFactoryBean automatically enables - * a PersistenceExceptionTranslationPostProcessor to translate Hibernate exceptions. - * - *

This class mainly serves as common base class for {@link LocalSessionFactoryBean}. - * For details on typical SessionFactory setup, see the LocalSessionFactoryBean javadoc. - * - * @author Juergen Hoeller - * @since 2.0 - * @see #setExposeTransactionAwareSessionFactory - * @see org.hibernate.SessionFactory#getCurrentSession() - * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor - */ -public abstract class AbstractSessionFactoryBean - implements FactoryBean, InitializingBean, DisposableBean, PersistenceExceptionTranslator { - - /** Logger available to subclasses */ - protected final Log logger = LogFactory.getLog(getClass()); - - private DataSource dataSource; - - private boolean useTransactionAwareDataSource = false; - - private boolean exposeTransactionAwareSessionFactory = true; - - private SQLExceptionTranslator jdbcExceptionTranslator; - - private SessionFactory sessionFactory; - - - /** - * Set the DataSource to be used by the SessionFactory. - * If set, this will override corresponding settings in Hibernate properties. - *

If this is set, the Hibernate settings should not define - * a connection provider to avoid meaningless double configuration. - *

If using HibernateTransactionManager as transaction strategy, consider - * proxying your target DataSource with a LazyConnectionDataSourceProxy. - * This defers fetching of an actual JDBC Connection until the first JDBC - * Statement gets executed, even within JDBC transactions (as performed by - * HibernateTransactionManager). Such lazy fetching is particularly beneficial - * for read-only operations, in particular if the chances of resolving the - * result in the second-level cache are high. - *

As JTA and transactional JNDI DataSources already provide lazy enlistment - * of JDBC Connections, LazyConnectionDataSourceProxy does not add value with - * JTA (i.e. Spring's JtaTransactionManager) as transaction strategy. - * @see #setUseTransactionAwareDataSource - * @see HibernateTransactionManager - * @see org.springframework.transaction.jta.JtaTransactionManager - * @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy - */ - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - } - - /** - * Return the DataSource to be used by the SessionFactory. - */ - public DataSource getDataSource() { - return this.dataSource; - } - - /** - * Set whether to use a transaction-aware DataSource for the SessionFactory, - * i.e. whether to automatically wrap the passed-in DataSource with Spring's - * TransactionAwareDataSourceProxy. - *

Default is "false": LocalSessionFactoryBean is usually used with Spring's - * HibernateTransactionManager or JtaTransactionManager, both of which work nicely - * on a plain JDBC DataSource. Hibernate Sessions and their JDBC Connections are - * fully managed by the Hibernate/JTA transaction infrastructure in such a scenario. - *

If you switch this flag to "true", Spring's Hibernate access will be able to - * participate in JDBC-based transactions managed outside of Hibernate - * (for example, by Spring's DataSourceTransactionManager). This can be convenient - * if you need a different local transaction strategy for another O/R mapping tool, - * for example, but still want Hibernate access to join into those transactions. - *

A further benefit of this option is that plain Sessions opened directly - * via the SessionFactory, outside of Spring's Hibernate support, will still - * participate in active Spring-managed transactions. However, consider using - * Hibernate's getCurrentSession() method instead (see javadoc of - * "exposeTransactionAwareSessionFactory" property). - *

WARNING: When using a transaction-aware JDBC DataSource in combination - * with OpenSessionInViewFilter/Interceptor, whether participating in JTA or - * external JDBC-based transactions, it is strongly recommended to set Hibernate's - * Connection release mode to "after_transaction" or "after_statement", which - * guarantees proper Connection handling in such a scenario. In contrast to that, - * HibernateTransactionManager generally requires release mode "on_close". - *

Note: If you want to use Hibernate's Connection release mode "after_statement" - * with a DataSource specified on this LocalSessionFactoryBean (for example, a - * JTA-aware DataSource fetched from JNDI), switch this setting to "true". - * Otherwise, the ConnectionProvider used underneath will vote against aggressive - * release and thus silently switch to release mode "after_transaction". - * @see #setDataSource - * @see #setExposeTransactionAwareSessionFactory - * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy - * @see org.springframework.jdbc.datasource.DataSourceTransactionManager - * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter - * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor - * @see HibernateTransactionManager - * @see org.springframework.transaction.jta.JtaTransactionManager - */ - public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) { - this.useTransactionAwareDataSource = useTransactionAwareDataSource; - } - - /** - * Return whether to use a transaction-aware DataSource for the SessionFactory. - */ - protected boolean isUseTransactionAwareDataSource() { - return this.useTransactionAwareDataSource; - } - - /** - * Set whether to expose a transaction-aware current Session from the - * SessionFactory's getCurrentSession() method, returning the - * Session that's associated with the current Spring-managed transaction, if any. - *

Default is "true", letting data access code work with the plain - * Hibernate SessionFactory and its getCurrentSession() method, - * while still being able to participate in current Spring-managed transactions: - * with any transaction management strategy, either local or JTA / EJB CMT, - * and any transaction synchronization mechanism, either Spring or JTA. - * Furthermore, getCurrentSession() will also seamlessly work with - * a request-scoped Session managed by OpenSessionInViewFilter/Interceptor. - *

Turn this flag off to expose the plain Hibernate SessionFactory with - * Hibernate's default getCurrentSession() behavior, supporting - * plain JTA synchronization only. Alternatively, simply override the - * corresponding Hibernate property "hibernate.current_session_context_class". - * @see SpringSessionContext - * @see org.hibernate.SessionFactory#getCurrentSession() - * @see org.springframework.transaction.jta.JtaTransactionManager - * @see HibernateTransactionManager - * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter - * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor - */ - public void setExposeTransactionAwareSessionFactory(boolean exposeTransactionAwareSessionFactory) { - this.exposeTransactionAwareSessionFactory = exposeTransactionAwareSessionFactory; - } - - /** - * Return whether to expose a transaction-aware proxy for the SessionFactory. - */ - protected boolean isExposeTransactionAwareSessionFactory() { - return this.exposeTransactionAwareSessionFactory; - } - - /** - * Set the JDBC exception translator for the SessionFactory, - * exposed via the PersistenceExceptionTranslator interface. - *

Applied to any SQLException root cause of a Hibernate JDBCException, - * overriding Hibernate's default SQLException translation (which is - * based on Hibernate's Dialect for a specific target database). - * @param jdbcExceptionTranslator the exception translator - * @see java.sql.SQLException - * @see org.hibernate.JDBCException - * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator - * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator - * @see org.springframework.dao.support.PersistenceExceptionTranslator - */ - public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) { - this.jdbcExceptionTranslator = jdbcExceptionTranslator; - } - - - /** - * Build and expose the SessionFactory. - * @see #buildSessionFactory() - * @see #wrapSessionFactoryIfNecessary - */ - public void afterPropertiesSet() throws Exception { - SessionFactory rawSf = buildSessionFactory(); - this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf); - afterSessionFactoryCreation(); - } - - /** - * Wrap the given SessionFactory with a proxy, if demanded. - *

The default implementation simply returns the given SessionFactory as-is. - * Subclasses may override this to implement transaction awareness through - * a SessionFactory proxy, for example. - * @param rawSf the raw SessionFactory as built by {@link #buildSessionFactory()} - * @return the SessionFactory reference to expose - * @see #buildSessionFactory() - */ - protected SessionFactory wrapSessionFactoryIfNecessary(SessionFactory rawSf) { - return rawSf; - } - - /** - * Return the exposed SessionFactory. - * Will throw an exception if not initialized yet. - * @return the SessionFactory (never null) - * @throws IllegalStateException if the SessionFactory has not been initialized yet - */ - protected final SessionFactory getSessionFactory() { - if (this.sessionFactory == null) { - throw new IllegalStateException("SessionFactory not initialized yet"); - } - return this.sessionFactory; - } - - /** - * Close the SessionFactory on bean factory shutdown. - */ - public void destroy() throws HibernateException { - logger.info("Closing Hibernate SessionFactory"); - try { - beforeSessionFactoryDestruction(); - } - finally { - this.sessionFactory.close(); - } - } - - - /** - * Return the singleton SessionFactory. - */ - public SessionFactory getObject() { - return this.sessionFactory; - } - - public Class getObjectType() { - return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class); - } - - public boolean isSingleton() { - return true; - } - - - /** - * Implementation of the PersistenceExceptionTranslator interface, - * as autodetected by Spring's PersistenceExceptionTranslationPostProcessor. - *

Converts the exception if it is a HibernateException; - * else returns null to indicate an unknown exception. - * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor - * @see #convertHibernateAccessException - */ - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { - if (ex instanceof HibernateException) { - return convertHibernateAccessException((HibernateException) ex); - } - return null; - } - - /** - * Convert the given HibernateException to an appropriate exception from the - * org.springframework.dao hierarchy. - *

Will automatically apply a specified SQLExceptionTranslator to a - * Hibernate JDBCException, else rely on Hibernate's default translation. - * @param ex HibernateException that occured - * @return a corresponding DataAccessException - * @see SessionFactoryUtils#convertHibernateAccessException - * @see #setJdbcExceptionTranslator - */ - protected DataAccessException convertHibernateAccessException(HibernateException ex) { - if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) { - JDBCException jdbcEx = (JDBCException) ex; - return this.jdbcExceptionTranslator.translate( - "Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException()); - } - return SessionFactoryUtils.convertHibernateAccessException(ex); - } - - - /** - * Build the underlying Hibernate SessionFactory. - * @return the raw SessionFactory (potentially to be wrapped with a - * transaction-aware proxy before it is exposed to the application) - * @throws Exception in case of initialization failure - */ - protected abstract SessionFactory buildSessionFactory() throws Exception; - - /** - * Hook that allows post-processing after the SessionFactory has been - * successfully created. The SessionFactory is already available through - * getSessionFactory() at this point. - *

This implementation is empty. - * @throws Exception in case of initialization failure - * @see #getSessionFactory() - */ - protected void afterSessionFactoryCreation() throws Exception { - } - - /** - * Hook that allows shutdown processing before the SessionFactory - * will be closed. The SessionFactory is still available through - * getSessionFactory() at this point. - *

This implementation is empty. - * @see #getSessionFactory() - */ - protected void beforeSessionFactoryDestruction() { - } - -} diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/FilterDefinitionFactoryBean.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/FilterDefinitionFactoryBean.java index 04b7789d3b8..3f84727aa01 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/FilterDefinitionFactoryBean.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/FilterDefinitionFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,24 +37,25 @@ import org.springframework.util.ReflectionUtils; * definition, as the list element for the "filterDefinitions" bean property. * For example: * - *

- * <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
+ * 
+ * {@code
+ * 
  *   ...
- *   <property name="filterDefinitions">
- *     <list>
- *       <bean class="org.springframework.orm.hibernate3.FilterDefinitionFactoryBean">
- *         <property name="filterName" value="myFilter"/>
- *         <property name="parameterTypes">
- *           <map>
- *             <entry key="myParam" value="string"/>
- *             <entry key="myOtherParam" value="long"/>
- *           </map>
- *         </property>
- *       </bean>
- *     </list>
- *   </property>
+ *   
+ *     
+ *       
+ *         
+ *         
+ *           
+ *             
+ *             
+ *           
+ *         
+ *       
+ *     
+ *   
  *   ...
- * </bean>
+ * }
* * Alternatively, specify a bean id (or name) attribute for the inner bean, * instead of the "filterName" property. @@ -73,7 +74,7 @@ public class FilterDefinitionFactoryBean implements FactoryBean trClass = FilterDefinitionFactoryBean.class.getClassLoader().loadClass( "org.hibernate.type.TypeResolver"); heuristicTypeMethod = trClass.getMethod("heuristicType", String.class); typeResolver = trClass.newInstance(); diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateAccessor.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateAccessor.java index 7fe337d6595..7b3b4e05a3b 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateAccessor.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateAccessor.java @@ -181,12 +181,13 @@ public abstract class HibernateAccessor implements InitializingBean, BeanFactory * property values before writing to and reading from the database. * Will get applied to any new Session created by this object. *

Such an interceptor can either be set at the SessionFactory level, - * i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on - * HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. - * It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager - * to avoid repeated configuration and guarantee consistent behavior in transactions. + * i.e. on SessionFactoryBuilder/LocalSessionFactoryBean, or at the Session + * level, i.e. on HibernateTemplate, HibernateInterceptor, and + * HibernateTransactionManager. It's preferable to set it on + * SessionFactoryBuilder or HibernateTransactionManager to avoid repeated + * configuration and guarantee consistent behavior in transactions. * @see #setEntityInterceptorBeanName - * @see LocalSessionFactoryBean#setEntityInterceptor + * @see SessionFactoryBuilder#setEntityInterceptor * @see HibernateTransactionManager#setEntityInterceptor */ public void setEntityInterceptor(Interceptor entityInterceptor) { @@ -208,7 +209,7 @@ public abstract class HibernateAccessor implements InitializingBean, BeanFactory if (this.beanFactory == null) { throw new IllegalStateException("Cannot get entity interceptor via bean name if no bean factory set"); } - return (Interceptor) this.beanFactory.getBean((String) this.entityInterceptor, Interceptor.class); + return this.beanFactory.getBean((String) this.entityInterceptor, Interceptor.class); } return (Interceptor) this.entityInterceptor; } @@ -272,7 +273,7 @@ public abstract class HibernateAccessor implements InitializingBean, BeanFactory * Sessions (for example, within a transaction). * @see #enableFilters(org.hibernate.Session) * @see org.hibernate.Session#enableFilter(String) - * @see LocalSessionFactoryBean#setFilterDefinitions + * @see SessionFactoryBuilder#setFilterDefinitions */ public void setFilterName(String filter) { this.filterNames = new String[] {filter}; @@ -287,7 +288,7 @@ public abstract class HibernateAccessor implements InitializingBean, BeanFactory * Sessions (for example, within a transaction). * @see #enableFilters(org.hibernate.Session) * @see org.hibernate.Session#enableFilter(String) - * @see LocalSessionFactoryBean#setFilterDefinitions + * @see SessionFactoryBuilder#setFilterDefinitions */ public void setFilterNames(String[] filterNames) { this.filterNames = filterNames; diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateConfigurationCallback.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateConfigurationCallback.java new file mode 100644 index 00000000000..1099d1894da --- /dev/null +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateConfigurationCallback.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate3; + +import org.hibernate.cfg.Configuration; + +/** + * Callback for use in conjunction with {@link SessionFactoryBuilderSupport#doWithConfiguration}. + * + * @author Chris Beams + * @since 3.1 + * @see SessionFactoryBuilderSupport#doWithConfiguration + * @see SessionFactoryBuilder + * @see org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBuilder + */ +public interface HibernateConfigurationCallback { + + /** + * Configure the given Hibernate {@code Configuration type}. Note that methods + * only setter methods should be called, and methods such as + * {@link Configuration#buildSessionFactory()} should be avoided. + * @throws Exception to propagate any exception thrown by + * {@code Configuration} methods + */ + void configure(C configuration) throws Exception; + +} diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTemplate.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTemplate.java index 72962e4087f..d8d0a542040 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTemplate.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,6 @@ import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Example; import org.hibernate.engine.SessionImplementor; import org.hibernate.event.EventSource; - import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.InvalidDataAccessApiUsageException; @@ -77,10 +76,10 @@ import org.springframework.util.Assert; * management, not participating in a custom Hibernate CurrentSessionContext * unless you explicitly switch {@link #setAllowCreate "allowCreate"} to "false". * - *

{@link LocalSessionFactoryBean} is the preferred way of obtaining a reference - * to a specific Hibernate SessionFactory, at least in a non-EJB environment. - * The Spring application context will manage its lifecycle, initializing and - * shutting down the factory as part of the application. + *

Using a {@link SessionFactoryBuilder} is the preferred way of obtaining a + * reference to a specific Hibernate SessionFactory, at least in a non-EJB + * environment. The Spring application context will manage its lifecycle, + * initializing and shutting down the factory as part of the application. * *

Note that operations that return an Iterator (i.e. iterate) * are supposed to be used within Spring-driven or JTA-driven transactions @@ -99,7 +98,7 @@ import org.springframework.util.Assert; * @see #setSessionFactory * @see HibernateCallback * @see org.hibernate.Session - * @see LocalSessionFactoryBean + * @see SessionFactoryBuilder * @see HibernateTransactionManager * @see org.springframework.transaction.jta.JtaTransactionManager * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java index ae5f3853b3c..d0910264d99 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java @@ -80,8 +80,9 @@ import org.springframework.transaction.support.TransactionSynchronizationManager * this instance needs to be aware of the DataSource ({@link #setDataSource}). * The given DataSource should obviously match the one used by the given * SessionFactory. To achieve this, configure both to the same JNDI DataSource, - * or preferably create the SessionFactory with {@link LocalSessionFactoryBean} and - * a local DataSource (which will be autodetected by this transaction manager). + * or preferably create the SessionFactory with {@link SessionFactoryBuilder} / + * {@link LocalSessionFactoryBean} and a local DataSource (which will be + * autodetected by this transaction manager). * *

JTA (usually through {@link org.springframework.transaction.jta.JtaTransactionManager}) * is necessary for accessing multiple transactional resources within the same @@ -116,6 +117,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager * @since 1.2 * @see #setSessionFactory * @see #setDataSource + * @see SessionFactoryBuilder * @see LocalSessionFactoryBean * @see SessionFactoryUtils#getSession * @see SessionFactoryUtils#applyTransactionTimeout @@ -129,6 +131,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager * @see org.springframework.jdbc.datasource.DataSourceTransactionManager * @see org.springframework.transaction.jta.JtaTransactionManager */ +@SuppressWarnings("serial") public class HibernateTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, BeanFactoryAware, InitializingBean { @@ -194,7 +197,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana * The DataSource should match the one used by the Hibernate SessionFactory: * for example, you could specify the same JNDI DataSource for both. *

If the SessionFactory was configured with LocalDataSourceConnectionProvider, - * i.e. by Spring's LocalSessionFactoryBean with a specified "dataSource", + * i.e. by Spring's SessionFactoryBuilder with a specified "dataSource", * the DataSource will be auto-detected: You can still explictly specify the * DataSource, but you don't need to in this case. *

A transactional JDBC Connection for this DataSource will be provided to @@ -208,7 +211,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana * unwrapped to extract its target DataSource. * @see #setAutodetectDataSource * @see LocalDataSourceConnectionProvider - * @see LocalSessionFactoryBean#setDataSource + * @see SessionFactoryBuilder#setDataSource * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy * @see org.springframework.jdbc.datasource.DataSourceUtils * @see org.springframework.jdbc.core.JdbcTemplate @@ -234,11 +237,11 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana /** * Set whether to autodetect a JDBC DataSource used by the Hibernate SessionFactory, - * if set via LocalSessionFactoryBean's setDataSource. Default is "true". + * if set via SessionFactoryBuilder's setDataSource. Default is "true". *

Can be turned off to deliberately ignore an available DataSource, in order * to not expose Hibernate transactions as JDBC transactions for that DataSource. * @see #setDataSource - * @see LocalSessionFactoryBean#setDataSource + * @see SessionFactoryBuilder#setDataSource */ public void setAutodetectDataSource(boolean autodetectDataSource) { this.autodetectDataSource = autodetectDataSource; @@ -329,11 +332,11 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana * property values before writing to and reading from the database. * Will get applied to any new Session created by this transaction manager. *

Such an interceptor can either be set at the SessionFactory level, - * i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on + * i.e. on SessionFactoryBuilder, or at the Session level, i.e. on * HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. - * It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager + * It's preferable to set it on SessionFactoryBuilder or HibernateTransactionManager * to avoid repeated configuration and guarantee consistent behavior in transactions. - * @see LocalSessionFactoryBean#setEntityInterceptor + * @see SessionFactoryBuilder#setEntityInterceptor * @see HibernateTemplate#setEntityInterceptor * @see HibernateInterceptor#setEntityInterceptor */ @@ -504,6 +507,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana logger.debug( "Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]"); } + @SuppressWarnings("deprecation") Connection con = session.connection(); Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); @@ -516,7 +520,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana "HibernateTransactionManager is not allowed to support custom isolation levels: " + "make sure that its 'prepareConnection' flag is on (the default) and that the " + "Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default). " + - "Make sure that your LocalSessionFactoryBean actually uses SpringTransactionFactory: Your " + + "Make sure that your SessionFactoryBuilder actually uses SpringTransactionFactory: Your " + "Hibernate properties should *not* include a 'hibernate.transaction.factory_class' property!"); } if (logger.isDebugEnabled()) { @@ -560,6 +564,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana // Register the Hibernate Session's JDBC Connection for the DataSource, if set. if (getDataSource() != null) { + @SuppressWarnings("deprecation") Connection con = session.connection(); ConnectionHolder conHolder = new ConnectionHolder(con); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { @@ -721,6 +726,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana // the isolation level and/or read-only flag of the JDBC Connection here. // Else, we need to rely on the connection pool to perform proper cleanup. try { + @SuppressWarnings("deprecation") Connection con = session.connection(); DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel()); } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalCacheProviderProxy.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalCacheProviderProxy.java index e240800e0f3..bc9c6a906c8 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalCacheProviderProxy.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalCacheProviderProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,16 +28,23 @@ import org.hibernate.cache.CacheProvider; * "cacheProvider" property. * * @author Juergen Hoeller + * @author Chris Beams * @since 2.5.1 * @see LocalSessionFactoryBean#setCacheProvider + * @see LocalRegionFactoryProxy + * @deprecated as of Spring 3.1 to reflect the deprecation of the + * CacheProvider SPI in Hibernate 3.3. Favor the new + * {@link org.hibernate.cache.RegionFactory} SPI and Spring's + * {@link LocalRegionFactoryProxy} support. */ +@Deprecated public class LocalCacheProviderProxy implements CacheProvider { private final CacheProvider cacheProvider; public LocalCacheProviderProxy() { - CacheProvider cp = LocalSessionFactoryBean.getConfigTimeCacheProvider(); + CacheProvider cp = SessionFactoryBeanDelegate.getConfigTimeCacheProvider(); // absolutely needs thread-bound CacheProvider to initialize if (cp == null) { throw new IllegalStateException("No Hibernate CacheProvider found - " + diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalDataSourceConnectionProvider.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalDataSourceConnectionProvider.java index 29a711b3337..0af65601b1b 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalDataSourceConnectionProvider.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalDataSourceConnectionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.springframework.orm.hibernate3; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; + import javax.sql.DataSource; import org.hibernate.HibernateException; @@ -28,12 +29,12 @@ import org.hibernate.util.JDBCExceptionReporter; /** * Hibernate connection provider for local DataSource instances * in an application context. This provider will be used if - * LocalSessionFactoryBean's "dataSource" property is set + * SessionFactoryBuilder's "dataSource" property is set * without a Hibernate TransactionManagerLookup. * * @author Juergen Hoeller * @since 1.2 - * @see LocalSessionFactoryBean#setDataSource + * @see SessionFactoryBuilder#setDataSource */ public class LocalDataSourceConnectionProvider implements ConnectionProvider { @@ -43,11 +44,11 @@ public class LocalDataSourceConnectionProvider implements ConnectionProvider { public void configure(Properties props) throws HibernateException { - this.dataSource = LocalSessionFactoryBean.getConfigTimeDataSource(); + this.dataSource = SessionFactoryBuilderSupport.getConfigTimeDataSource(); // absolutely needs thread-bound DataSource to initialize if (this.dataSource == null) { throw new HibernateException("No local DataSource found for configuration - " + - "'dataSource' property must be set on LocalSessionFactoryBean"); + "'dataSource' property must be set on SessionFactoryBuilder"); } this.dataSourceToUse = getDataSourceToUse(this.dataSource); } @@ -56,10 +57,10 @@ public class LocalDataSourceConnectionProvider implements ConnectionProvider { * Return the DataSource to use for retrieving Connections. *

This implementation returns the passed-in DataSource as-is. * @param originalDataSource the DataSource as configured by the user - * on LocalSessionFactoryBean + * on SessionFactoryBuilder * @return the DataSource to actually retrieve Connections from * (potentially wrapped) - * @see LocalSessionFactoryBean#setDataSource + * @see SessionFactoryBuilder#setDataSource */ protected DataSource getDataSourceToUse(DataSource originalDataSource) { return originalDataSource; diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalJtaDataSourceConnectionProvider.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalJtaDataSourceConnectionProvider.java index b2da29349e9..c4f204d0aa7 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalJtaDataSourceConnectionProvider.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalJtaDataSourceConnectionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package org.springframework.orm.hibernate3; /** * Subclass of LocalDataSourceConnectionProvider that will be used - * if LocalSessionFactoryBean's "dataSource" property is set + * if SessionFactoryBean's "dataSource" property is set * in combination with a Hibernate TransactionManagerLookup. * * @author Juergen Hoeller diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalRegionFactoryProxy.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalRegionFactoryProxy.java index d43196ce3fb..6e3c1d5b717 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalRegionFactoryProxy.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalRegionFactoryProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ import org.springframework.util.ReflectionUtils; /** * Proxy for a Hibernate RegionFactory, delegating to a Spring-managed - * RegionFactory instance, determined by LocalSessionFactoryBean's + * RegionFactory instance, determined by SessionFactoryBuilder's * "cacheRegionFactory" property. * *

Compatible with Hibernate 3.3 as well as Hibernate 3.5's version @@ -41,7 +41,7 @@ import org.springframework.util.ReflectionUtils; * * @author Juergen Hoeller * @since 3.0 - * @see LocalSessionFactoryBean#setCacheRegionFactory + * @see SessionFactoryBuilder#setCacheRegionFactory */ public class LocalRegionFactoryProxy implements RegionFactory { @@ -52,11 +52,11 @@ public class LocalRegionFactoryProxy implements RegionFactory { * Standard constructor. */ public LocalRegionFactoryProxy() { - RegionFactory rf = (RegionFactory) LocalSessionFactoryBean.getConfigTimeRegionFactory(); + RegionFactory rf = (RegionFactory) SessionFactoryBuilderSupport.getConfigTimeRegionFactory(); // absolutely needs thread-bound RegionFactory to initialize if (rf == null) { throw new IllegalStateException("No Hibernate RegionFactory found - " + - "'cacheRegionFactory' property must be set on LocalSessionFactoryBean"); + "'cacheRegionFactory' property must be set on SessionFactoryBuilder"); } this.regionFactory = rf; } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalSessionFactoryBean.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalSessionFactoryBean.java index 0cb0d7335f1..40786470aba 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalSessionFactoryBean.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalSessionFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,1109 +16,125 @@ package org.springframework.orm.hibernate3; -import java.io.File; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Collection; -import java.util.Enumeration; -import java.util.Map; -import java.util.Properties; -import javax.sql.DataSource; -import javax.transaction.TransactionManager; - import org.hibernate.HibernateException; -import org.hibernate.Interceptor; -import org.hibernate.Session; -import org.hibernate.SessionFactory; -import org.hibernate.cache.CacheProvider; -import org.hibernate.cfg.Configuration; -import org.hibernate.cfg.Environment; -import org.hibernate.cfg.NamingStrategy; -import org.hibernate.dialect.Dialect; -import org.hibernate.engine.FilterDefinition; -import org.hibernate.event.EventListeners; -import org.hibernate.tool.hbm2ddl.DatabaseMetadata; -import org.hibernate.transaction.JTATransactionFactory; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; +import org.hibernate.SessionFactory; import org.springframework.dao.DataAccessException; -import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; -import org.springframework.jdbc.support.JdbcUtils; -import org.springframework.jdbc.support.lob.LobHandler; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; +import org.springframework.jdbc.support.SQLExceptionTranslator; /** - * {@link org.springframework.beans.factory.FactoryBean} that creates a - * Hibernate {@link org.hibernate.SessionFactory}. This is the usual way to - * set up a shared Hibernate SessionFactory in a Spring application context; - * the SessionFactory can then be passed to Hibernate-based DAOs via - * dependency injection. - * - *

Configuration settings can either be read from a Hibernate XML file, - * specified as "configLocation", or completely via this class. A typical - * local configuration consists of one or more "mappingResources", various - * "hibernateProperties" (not strictly necessary), and a "dataSource" that the - * SessionFactory should use. The latter can also be specified via Hibernate - * properties, but "dataSource" supports any Spring-configured DataSource, - * instead of relying on Hibernate's own connection providers. + * Subclass of {@link SessionFactoryBuilder} adhering to Spring's + * {@link org.springframework.beans.factory.FactoryBean FactoryBean} contract, + * making it suitable for use in XML configuration. * - *

This SessionFactory handling strategy is appropriate for most types of - * applications, from Hibernate-only single database apps to ones that need - * distributed transactions. Either {@link HibernateTransactionManager} or - * {@link org.springframework.transaction.jta.JtaTransactionManager} can be - * used for transaction demarcation, with the latter only necessary for - * transactions which span multiple databases. + *

A typical {@code LocalSessionFactoryBean} bean definition: * - *

This factory bean will by default expose a transaction-aware SessionFactory - * proxy, letting data access code work with the plain Hibernate SessionFactory - * and its getCurrentSession() method, while still being able to - * participate in current Spring-managed transactions: with any transaction - * management strategy, either local or JTA / EJB CMT, and any transaction - * synchronization mechanism, either Spring or JTA. Furthermore, - * getCurrentSession() will also seamlessly work with - * a request-scoped Session managed by - * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter} / - * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}. + *

+ * {@code
+ * 
+ *   
+ *   
+ * }
* - *

Requires Hibernate 3.2 or later; tested with 3.3, 3.5 and 3.6. - * Note that this factory will use "on_close" as default Hibernate connection - * release mode, unless in the case of a "jtaTransactionManager" specified, - * for the reason that this is appropriate for most Spring-based applications - * (in particular when using Spring's HibernateTransactionManager). + *

Implements the + * {@link org.springframework.dao.support.PersistenceExceptionTranslator + * PersistenceExceptionTranslator} interface, as autodetected by Spring's {@link + * org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor + * PersistenceExceptionTranslationPostProcessor}, for AOP-based translation of + * native Hibernate exceptions to Spring's {@link DataAccessException} hierarchy. + * Hence, the presence of an {@code LocalSessionFactoryBean} automatically + * enables a {@code PersistenceExceptionTranslationPostProcessor} to translate + * Hibernate exceptions. * * @author Juergen Hoeller + * @author Chris Beams * @since 1.2 - * @see HibernateTemplate#setSessionFactory - * @see HibernateTransactionManager#setSessionFactory - * @see #setExposeTransactionAwareSessionFactory - * @see #setJtaTransactionManager - * @see org.hibernate.SessionFactory#getCurrentSession() - * @see HibernateTransactionManager + * @see SessionFactoryBuilderSupport + * @see SessionFactoryBeanOperations + * @see org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean */ -public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware { - - private static final ThreadLocal configTimeDataSourceHolder = - new ThreadLocal(); - - private static final ThreadLocal configTimeTransactionManagerHolder = - new ThreadLocal(); - - private static final ThreadLocal configTimeRegionFactoryHolder = - new ThreadLocal(); - - private static final ThreadLocal configTimeCacheProviderHolder = - new ThreadLocal(); - - private static final ThreadLocal configTimeLobHandlerHolder = - new ThreadLocal(); - - /** - * Return the DataSource for the currently configured Hibernate SessionFactory, - * to be used by LocalDataSourceConnectionProvoder. - *

This instance will be set before initialization of the corresponding - * SessionFactory, and reset immediately afterwards. It is thus only available - * during configuration. - * @see #setDataSource - * @see LocalDataSourceConnectionProvider - */ - public static DataSource getConfigTimeDataSource() { - return configTimeDataSourceHolder.get(); - } - - /** - * Return the JTA TransactionManager for the currently configured Hibernate - * SessionFactory, to be used by LocalTransactionManagerLookup. - *

This instance will be set before initialization of the corresponding - * SessionFactory, and reset immediately afterwards. It is thus only available - * during configuration. - * @see #setJtaTransactionManager - * @see LocalTransactionManagerLookup - */ - public static TransactionManager getConfigTimeTransactionManager() { - return configTimeTransactionManagerHolder.get(); - } - - /** - * Return the RegionFactory for the currently configured Hibernate SessionFactory, - * to be used by LocalRegionFactoryProxy. - *

This instance will be set before initialization of the corresponding - * SessionFactory, and reset immediately afterwards. It is thus only available - * during configuration. - * @see #setCacheRegionFactory - */ - static Object getConfigTimeRegionFactory() { - return configTimeRegionFactoryHolder.get(); - } - - /** - * Return the CacheProvider for the currently configured Hibernate SessionFactory, - * to be used by LocalCacheProviderProxy. - *

This instance will be set before initialization of the corresponding - * SessionFactory, and reset immediately afterwards. It is thus only available - * during configuration. - * @see #setCacheProvider - */ - public static CacheProvider getConfigTimeCacheProvider() { - return configTimeCacheProviderHolder.get(); - } - - /** - * Return the LobHandler for the currently configured Hibernate SessionFactory, - * to be used by UserType implementations like ClobStringType. - *

This instance will be set before initialization of the corresponding - * SessionFactory, and reset immediately afterwards. It is thus only available - * during configuration. - * @see #setLobHandler - * @see org.springframework.orm.hibernate3.support.ClobStringType - * @see org.springframework.orm.hibernate3.support.BlobByteArrayType - * @see org.springframework.orm.hibernate3.support.BlobSerializableType - */ - public static LobHandler getConfigTimeLobHandler() { - return configTimeLobHandlerHolder.get(); - } - - - private Class configurationClass = Configuration.class; - - private Resource[] configLocations; - - private String[] mappingResources; - - private Resource[] mappingLocations; - - private Resource[] cacheableMappingLocations; - - private Resource[] mappingJarLocations; - - private Resource[] mappingDirectoryLocations; - - private Properties hibernateProperties; - - private TransactionManager jtaTransactionManager; - - private Object cacheRegionFactory; - - private CacheProvider cacheProvider; - - private LobHandler lobHandler; - - private Interceptor entityInterceptor; - - private NamingStrategy namingStrategy; - - private TypeDefinitionBean[] typeDefinitions; - - private FilterDefinition[] filterDefinitions; - - private Properties entityCacheStrategies; - - private Properties collectionCacheStrategies; - - private Map eventListeners; - - private boolean schemaUpdate = false; - - private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); - - private Configuration configuration; - - - /** - * Specify the Hibernate Configuration class to use. - * Default is "org.hibernate.cfg.Configuration"; any subclass of - * this default Hibernate Configuration class can be specified. - *

Can be set to "org.hibernate.cfg.AnnotationConfiguration" for - * using Hibernate3 annotation support (initially only available as - * alpha download separate from the main Hibernate3 distribution). - *

Annotated packages and annotated classes can be specified via the - * corresponding tags in "hibernate.cfg.xml" then, so this will usually - * be combined with a "configLocation" property that points at such a - * standard Hibernate configuration file. - * @see #setConfigLocation - * @see org.hibernate.cfg.Configuration - * @see org.hibernate.cfg.AnnotationConfiguration - */ - @SuppressWarnings("unchecked") - public void setConfigurationClass(Class configurationClass) { - if (configurationClass == null || !Configuration.class.isAssignableFrom(configurationClass)) { - throw new IllegalArgumentException( - "'configurationClass' must be assignable to [org.hibernate.cfg.Configuration]"); - } - this.configurationClass = (Class) configurationClass; - } - - /** - * Set the location of a single Hibernate XML config file, for example as - * classpath resource "classpath:hibernate.cfg.xml". - *

Note: Can be omitted when all necessary properties and mapping - * resources are specified locally via this bean. - * @see org.hibernate.cfg.Configuration#configure(java.net.URL) - */ - public void setConfigLocation(Resource configLocation) { - this.configLocations = new Resource[] {configLocation}; - } - - /** - * Set the locations of multiple Hibernate XML config files, for example as - * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml". - *

Note: Can be omitted when all necessary properties and mapping - * resources are specified locally via this bean. - * @see org.hibernate.cfg.Configuration#configure(java.net.URL) - */ - public void setConfigLocations(Resource[] configLocations) { - this.configLocations = configLocations; - } - - /** - * Set Hibernate mapping resources to be found in the class path, - * like "example.hbm.xml" or "mypackage/example.hbm.xml". - * Analogous to mapping entries in a Hibernate XML config file. - * Alternative to the more generic setMappingLocations method. - *

Can be used to add to mappings from a Hibernate XML config file, - * or to specify all mappings locally. - * @see #setMappingLocations - * @see org.hibernate.cfg.Configuration#addResource - */ - public void setMappingResources(String[] mappingResources) { - this.mappingResources = mappingResources; - } - - /** - * Set locations of Hibernate mapping files, for example as classpath - * resource "classpath:example.hbm.xml". Supports any resource location - * via Spring's resource abstraction, for example relative paths like - * "WEB-INF/mappings/example.hbm.xml" when running in an application context. - *

Can be used to add to mappings from a Hibernate XML config file, - * or to specify all mappings locally. - * @see org.hibernate.cfg.Configuration#addInputStream - */ - public void setMappingLocations(Resource[] mappingLocations) { - this.mappingLocations = mappingLocations; - } - - /** - * Set locations of cacheable Hibernate mapping files, for example as web app - * resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location - * via Spring's resource abstraction, as long as the resource can be resolved - * in the file system. - *

Can be used to add to mappings from a Hibernate XML config file, - * or to specify all mappings locally. - * @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File) - */ - public void setCacheableMappingLocations(Resource[] cacheableMappingLocations) { - this.cacheableMappingLocations = cacheableMappingLocations; - } - - /** - * Set locations of jar files that contain Hibernate mapping resources, - * like "WEB-INF/lib/example.hbm.jar". - *

Can be used to add to mappings from a Hibernate XML config file, - * or to specify all mappings locally. - * @see org.hibernate.cfg.Configuration#addJar(java.io.File) - */ - public void setMappingJarLocations(Resource[] mappingJarLocations) { - this.mappingJarLocations = mappingJarLocations; - } - - /** - * Set locations of directories that contain Hibernate mapping resources, - * like "WEB-INF/mappings". - *

Can be used to add to mappings from a Hibernate XML config file, - * or to specify all mappings locally. - * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File) - */ - public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) { - this.mappingDirectoryLocations = mappingDirectoryLocations; - } - - /** - * Set Hibernate properties, such as "hibernate.dialect". - *

Can be used to override values in a Hibernate XML config file, - * or to specify all necessary properties locally. - *

Note: Do not specify a transaction provider here when using - * Spring-driven transactions. It is also advisable to omit connection - * provider settings and use a Spring-set DataSource instead. - * @see #setDataSource - */ - public void setHibernateProperties(Properties hibernateProperties) { - this.hibernateProperties = hibernateProperties; - } - - /** - * Return the Hibernate properties, if any. Mainly available for - * configuration through property paths that specify individual keys. - */ - public Properties getHibernateProperties() { - if (this.hibernateProperties == null) { - this.hibernateProperties = new Properties(); - } - return this.hibernateProperties; - } - - /** - * Set the JTA TransactionManager to be used for Hibernate's - * TransactionManagerLookup. Allows for using a Spring-managed - * JTA TransactionManager for Hibernate's cache synchronization. - *

Note: If this is set, the Hibernate settings should not define a - * transaction manager lookup to avoid meaningless double configuration. - * @see LocalTransactionManagerLookup - */ - public void setJtaTransactionManager(TransactionManager jtaTransactionManager) { - this.jtaTransactionManager = jtaTransactionManager; - } +public class LocalSessionFactoryBean extends SessionFactoryBuilder implements SessionFactoryBeanOperations { - /** - * Set the Hibernate RegionFactory to use for the SessionFactory. - * Allows for using a Spring-managed RegionFactory instance. - *

As of Hibernate 3.3, this is the preferred mechanism for configuring - * caches, superseding the {@link #setCacheProvider CacheProvider SPI}. - * For Hibernate 3.2 compatibility purposes, the accepted reference is of type - * Object: the actual type is org.hibernate.cache.RegionFactory. - *

Note: If this is set, the Hibernate settings should not define a - * cache provider to avoid meaningless double configuration. - * @see org.hibernate.cache.RegionFactory - */ - public void setCacheRegionFactory(Object cacheRegionFactory) { - this.cacheRegionFactory = cacheRegionFactory; - } + private final SessionFactoryBeanDelegate delegate = new SessionFactoryBeanDelegate(this); - /** - * Set the Hibernate CacheProvider to use for the SessionFactory. - * Allows for using a Spring-managed CacheProvider instance. - *

Note: If this is set, the Hibernate settings should not define a - * cache provider to avoid meaningless double configuration. - * @deprecated as of Spring 3.0, following Hibernate 3.3's deprecation - * of the CacheProvider SPI - * @see #setCacheRegionFactory - */ @Deprecated - public void setCacheProvider(CacheProvider cacheProvider) { - this.cacheProvider = cacheProvider; - } - - /** - * Set the LobHandler to be used by the SessionFactory. - * Will be exposed at config time for UserType implementations. - * @see #getConfigTimeLobHandler - * @see org.hibernate.usertype.UserType - * @see org.springframework.orm.hibernate3.support.ClobStringType - * @see org.springframework.orm.hibernate3.support.BlobByteArrayType - * @see org.springframework.orm.hibernate3.support.BlobSerializableType - */ - public void setLobHandler(LobHandler lobHandler) { - this.lobHandler = lobHandler; - } - - /** - * Set a Hibernate entity interceptor that allows to inspect and change - * property values before writing to and reading from the database. - * Will get applied to any new Session created by this factory. - *

Such an interceptor can either be set at the SessionFactory level, i.e. on - * LocalSessionFactoryBean, or at the Session level, i.e. on HibernateTemplate, - * HibernateInterceptor, and HibernateTransactionManager. It's preferable to set - * it on LocalSessionFactoryBean or HibernateTransactionManager to avoid repeated - * configuration and guarantee consistent behavior in transactions. - * @see HibernateTemplate#setEntityInterceptor - * @see HibernateInterceptor#setEntityInterceptor - * @see HibernateTransactionManager#setEntityInterceptor - * @see org.hibernate.cfg.Configuration#setInterceptor - */ - public void setEntityInterceptor(Interceptor entityInterceptor) { - this.entityInterceptor = entityInterceptor; - } - - /** - * Set a Hibernate NamingStrategy for the SessionFactory, determining the - * physical column and table names given the info in the mapping document. - * @see org.hibernate.cfg.Configuration#setNamingStrategy - */ - public void setNamingStrategy(NamingStrategy namingStrategy) { - this.namingStrategy = namingStrategy; - } - - /** - * Specify the Hibernate type definitions to register with the SessionFactory, - * as Spring TypeDefinitionBean instances. This is an alternative to specifying - * <<typedef> elements in Hibernate mapping files. - *

Unfortunately, Hibernate itself does not define a complete object that - * represents a type definition, hence the need for Spring's TypeDefinitionBean. - * @see TypeDefinitionBean - * @see org.hibernate.cfg.Mappings#addTypeDef(String, String, java.util.Properties) - */ - public void setTypeDefinitions(TypeDefinitionBean[] typeDefinitions) { - this.typeDefinitions = typeDefinitions; - } - - /** - * Specify the Hibernate FilterDefinitions to register with the SessionFactory. - * This is an alternative to specifying <<filter-def> elements in - * Hibernate mapping files. - *

Typically, the passed-in FilterDefinition objects will have been defined - * as Spring FilterDefinitionFactoryBeans, probably as inner beans within the - * LocalSessionFactoryBean definition. - * @see FilterDefinitionFactoryBean - * @see org.hibernate.cfg.Configuration#addFilterDefinition - */ - public void setFilterDefinitions(FilterDefinition[] filterDefinitions) { - this.filterDefinitions = filterDefinitions; + public void setCacheProvider(org.hibernate.cache.CacheProvider cacheProvider) { + delegate.setCacheProvider(cacheProvider); } - /** - * Specify the cache strategies for entities (persistent classes or named entities). - * This configuration setting corresponds to the <class-cache> entry - * in the "hibernate.cfg.xml" configuration format. - *

For example: - *

-	 * <property name="entityCacheStrategies">
-	 *   <props>
-	 *     <prop key="com.mycompany.Customer">read-write</prop>
-	 *     <prop key="com.mycompany.Product">read-only,myRegion</prop>
-	 *   </props>
-	 * </property>
- * @param entityCacheStrategies properties that define entity cache strategies, - * with class names as keys and cache concurrency strategies as values - * @see org.hibernate.cfg.Configuration#setCacheConcurrencyStrategy(String, String) - */ - public void setEntityCacheStrategies(Properties entityCacheStrategies) { - this.entityCacheStrategies = entityCacheStrategies; - } - - /** - * Specify the cache strategies for persistent collections (with specific roles). - * This configuration setting corresponds to the <collection-cache> entry - * in the "hibernate.cfg.xml" configuration format. - *

For example: - *

-	 * <property name="collectionCacheStrategies">
-	 *   <props>
-	 *     <prop key="com.mycompany.Order.items">read-write</prop>
-	 *     <prop key="com.mycompany.Product.categories">read-only,myRegion</prop>
-	 *   </props>
-	 * </property>
- * @param collectionCacheStrategies properties that define collection cache strategies, - * with collection roles as keys and cache concurrency strategies as values - * @see org.hibernate.cfg.Configuration#setCollectionCacheConcurrencyStrategy(String, String) - */ - public void setCollectionCacheStrategies(Properties collectionCacheStrategies) { - this.collectionCacheStrategies = collectionCacheStrategies; - } - - /** - * Specify the Hibernate event listeners to register, with listener types - * as keys and listener objects as values. Instead of a single listener object, - * you can also pass in a list or set of listeners objects as value. - *

See the Hibernate documentation for further details on listener types - * and associated listener interfaces. - * @param eventListeners Map with listener type Strings as keys and - * listener objects as values - * @see org.hibernate.cfg.Configuration#setListener(String, Object) - */ - public void setEventListeners(Map eventListeners) { - this.eventListeners = eventListeners; - } - - /** - * Set whether to execute a schema update after SessionFactory initialization. - *

For details on how to make schema update scripts work, see the Hibernate - * documentation, as this class leverages the same schema update script support - * in org.hibernate.cfg.Configuration as Hibernate's own SchemaUpdate tool. - * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript - * @see org.hibernate.tool.hbm2ddl.SchemaUpdate - */ - public void setSchemaUpdate(boolean schemaUpdate) { - this.schemaUpdate = schemaUpdate; - } - - public void setBeanClassLoader(ClassLoader beanClassLoader) { - this.beanClassLoader = beanClassLoader; - } - - @Override - @SuppressWarnings("unchecked") - protected SessionFactory buildSessionFactory() throws Exception { - // Create Configuration instance. - Configuration config = newConfiguration(); - - DataSource dataSource = getDataSource(); - if (dataSource != null) { - // Make given DataSource available for SessionFactory configuration. - configTimeDataSourceHolder.set(dataSource); - } - if (this.jtaTransactionManager != null) { - // Make Spring-provided JTA TransactionManager available. - configTimeTransactionManagerHolder.set(this.jtaTransactionManager); - } - if (this.cacheRegionFactory != null) { - // Make Spring-provided Hibernate RegionFactory available. - configTimeRegionFactoryHolder.set(this.cacheRegionFactory); - } - if (this.cacheProvider != null) { - // Make Spring-provided Hibernate CacheProvider available. - configTimeCacheProviderHolder.set(this.cacheProvider); - } - if (this.lobHandler != null) { - // Make given LobHandler available for SessionFactory configuration. - // Do early because because mapping resource might refer to custom types. - configTimeLobHandlerHolder.set(this.lobHandler); - } - - // Analogous to Hibernate EntityManager's Ejb3Configuration: - // Hibernate doesn't allow setting the bean ClassLoader explicitly, - // so we need to expose it as thread context ClassLoader accordingly. - Thread currentThread = Thread.currentThread(); - ClassLoader threadContextClassLoader = currentThread.getContextClassLoader(); - boolean overrideClassLoader = - (this.beanClassLoader != null && !this.beanClassLoader.equals(threadContextClassLoader)); - if (overrideClassLoader) { - currentThread.setContextClassLoader(this.beanClassLoader); - } - - try { - if (isExposeTransactionAwareSessionFactory()) { - // Set Hibernate 3.1+ CurrentSessionContext implementation, - // providing the Spring-managed Session as current Session. - // Can be overridden by a custom value for the corresponding Hibernate property. - config.setProperty( - Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName()); - } - - if (this.jtaTransactionManager != null) { - // Set Spring-provided JTA TransactionManager as Hibernate property. - config.setProperty( - Environment.TRANSACTION_STRATEGY, JTATransactionFactory.class.getName()); - config.setProperty( - Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName()); - } - else { - // Makes the Hibernate Session aware of the presence of a Spring-managed transaction. - // Also sets connection release mode to ON_CLOSE by default. - config.setProperty( - Environment.TRANSACTION_STRATEGY, SpringTransactionFactory.class.getName()); - } - - if (this.entityInterceptor != null) { - // Set given entity interceptor at SessionFactory level. - config.setInterceptor(this.entityInterceptor); - } - - if (this.namingStrategy != null) { - // Pass given naming strategy to Hibernate Configuration. - config.setNamingStrategy(this.namingStrategy); - } - - if (this.typeDefinitions != null) { - // Register specified Hibernate type definitions. - // Use reflection for compatibility with both Hibernate 3.3 and 3.5: - // the returned Mappings object changed from a class to an interface. - Method createMappings = Configuration.class.getMethod("createMappings"); - Method addTypeDef = createMappings.getReturnType().getMethod( - "addTypeDef", String.class, String.class, Properties.class); - Object mappings = ReflectionUtils.invokeMethod(createMappings, config); - for (TypeDefinitionBean typeDef : this.typeDefinitions) { - ReflectionUtils.invokeMethod(addTypeDef, mappings, - typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters()); - } - } - - if (this.filterDefinitions != null) { - // Register specified Hibernate FilterDefinitions. - for (FilterDefinition filterDef : this.filterDefinitions) { - config.addFilterDefinition(filterDef); - } - } - - if (this.configLocations != null) { - for (Resource resource : this.configLocations) { - // Load Hibernate configuration from given location. - config.configure(resource.getURL()); - } - } - - if (this.hibernateProperties != null) { - // Add given Hibernate properties to Configuration. - config.addProperties(this.hibernateProperties); - } - - if (dataSource != null) { - Class providerClass = LocalDataSourceConnectionProvider.class; - if (isUseTransactionAwareDataSource() || dataSource instanceof TransactionAwareDataSourceProxy) { - providerClass = TransactionAwareDataSourceConnectionProvider.class; - } - else if (config.getProperty(Environment.TRANSACTION_MANAGER_STRATEGY) != null) { - providerClass = LocalJtaDataSourceConnectionProvider.class; - } - // Set Spring-provided DataSource as Hibernate ConnectionProvider. - config.setProperty(Environment.CONNECTION_PROVIDER, providerClass.getName()); - } - - if (this.cacheRegionFactory != null) { - // Expose Spring-provided Hibernate RegionFactory. - config.setProperty(Environment.CACHE_REGION_FACTORY, - "org.springframework.orm.hibernate3.LocalRegionFactoryProxy"); - } - else if (this.cacheProvider != null) { - // Expose Spring-provided Hibernate CacheProvider. - config.setProperty(Environment.CACHE_PROVIDER, LocalCacheProviderProxy.class.getName()); - } - - if (this.mappingResources != null) { - // Register given Hibernate mapping definitions, contained in resource files. - for (String mapping : this.mappingResources) { - Resource resource = new ClassPathResource(mapping.trim(), this.beanClassLoader); - config.addInputStream(resource.getInputStream()); - } - } - - if (this.mappingLocations != null) { - // Register given Hibernate mapping definitions, contained in resource files. - for (Resource resource : this.mappingLocations) { - config.addInputStream(resource.getInputStream()); - } - } - - if (this.cacheableMappingLocations != null) { - // Register given cacheable Hibernate mapping definitions, read from the file system. - for (Resource resource : this.cacheableMappingLocations) { - config.addCacheableFile(resource.getFile()); - } - } - - if (this.mappingJarLocations != null) { - // Register given Hibernate mapping definitions, contained in jar files. - for (Resource resource : this.mappingJarLocations) { - config.addJar(resource.getFile()); - } - } - - if (this.mappingDirectoryLocations != null) { - // Register all Hibernate mapping definitions in the given directories. - for (Resource resource : this.mappingDirectoryLocations) { - File file = resource.getFile(); - if (!file.isDirectory()) { - throw new IllegalArgumentException( - "Mapping directory location [" + resource + "] does not denote a directory"); - } - config.addDirectory(file); - } - } - - // Tell Hibernate to eagerly compile the mappings that we registered, - // for availability of the mapping information in further processing. - postProcessMappings(config); - config.buildMappings(); - - if (this.entityCacheStrategies != null) { - // Register cache strategies for mapped entities. - for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) { - String className = (String) classNames.nextElement(); - String[] strategyAndRegion = - StringUtils.commaDelimitedListToStringArray(this.entityCacheStrategies.getProperty(className)); - if (strategyAndRegion.length > 1) { - // method signature declares return type as Configuration on Hibernate 3.6 - // but as void on Hibernate 3.3 and 3.5 - Method setCacheConcurrencyStrategy = Configuration.class.getMethod( - "setCacheConcurrencyStrategy", String.class, String.class, String.class); - ReflectionUtils.invokeMethod(setCacheConcurrencyStrategy, config, - className, strategyAndRegion[0], strategyAndRegion[1]); - } - else if (strategyAndRegion.length > 0) { - config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]); - } - } - } - - if (this.collectionCacheStrategies != null) { - // Register cache strategies for mapped collections. - for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) { - String collRole = (String) collRoles.nextElement(); - String[] strategyAndRegion = - StringUtils.commaDelimitedListToStringArray(this.collectionCacheStrategies.getProperty(collRole)); - if (strategyAndRegion.length > 1) { - config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0], strategyAndRegion[1]); - } - else if (strategyAndRegion.length > 0) { - config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]); - } - } - } - - if (this.eventListeners != null) { - // Register specified Hibernate event listeners. - for (Map.Entry entry : this.eventListeners.entrySet()) { - String listenerType = entry.getKey(); - Object listenerObject = entry.getValue(); - if (listenerObject instanceof Collection) { - Collection listeners = (Collection) listenerObject; - EventListeners listenerRegistry = config.getEventListeners(); - Object[] listenerArray = - (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size()); - listenerArray = listeners.toArray(listenerArray); - config.setListeners(listenerType, listenerArray); - } - else { - config.setListener(listenerType, listenerObject); - } - } - } - - // Perform custom post-processing in subclasses. - postProcessConfiguration(config); - - // Build SessionFactory instance. - logger.info("Building new Hibernate SessionFactory"); - this.configuration = config; - return newSessionFactory(config); - } - - finally { - if (dataSource != null) { - configTimeDataSourceHolder.remove(); - } - if (this.jtaTransactionManager != null) { - configTimeTransactionManagerHolder.remove(); - } - if (this.cacheRegionFactory != null) { - configTimeCacheProviderHolder.remove(); - } - if (this.cacheProvider != null) { - configTimeCacheProviderHolder.remove(); - } - if (this.lobHandler != null) { - configTimeLobHandlerHolder.remove(); - } - if (overrideClassLoader) { - // Reset original thread context ClassLoader. - currentThread.setContextClassLoader(threadContextClassLoader); - } - } + protected final void preBuildSessionFactory() { + delegate.preBuildSessionFactory(); } - /** - * Subclasses can override this method to perform custom initialization - * of the Configuration instance used for SessionFactory creation. - * The properties of this LocalSessionFactoryBean will be applied to - * the Configuration object that gets returned here. - *

The default implementation creates a new Configuration instance. - * A custom implementation could prepare the instance in a specific way, - * or use a custom Configuration subclass. - * @return the Configuration instance - * @throws HibernateException in case of Hibernate initialization errors - * @see org.hibernate.cfg.Configuration#Configuration() - */ - protected Configuration newConfiguration() throws HibernateException { - return BeanUtils.instantiateClass(this.configurationClass); + @Override + protected final void postBuildSessionFactory() { + delegate.postBuildSessionFactory(); } - /** - * To be implemented by subclasses that want to to register further mappings - * on the Configuration object after this FactoryBean registered its specified - * mappings. - *

Invoked before the Configuration.buildMappings() call, - * so that it can still extend and modify the mapping information. - * @param config the current Configuration object - * @throws HibernateException in case of Hibernate initialization errors - * @see org.hibernate.cfg.Configuration#buildMappings() - */ - protected void postProcessMappings(Configuration config) throws HibernateException { + public void destroy() throws HibernateException { + delegate.destroy(); } - /** - * To be implemented by subclasses that want to to perform custom - * post-processing of the Configuration object after this FactoryBean - * performed its default initialization. - *

Invoked after the Configuration.buildMappings() call, - * so that it can operate on the completed and fully parsed mapping information. - * @param config the current Configuration object - * @throws HibernateException in case of Hibernate initialization errors - * @see org.hibernate.cfg.Configuration#buildMappings() - */ - protected void postProcessConfiguration(Configuration config) throws HibernateException { + public SessionFactory getObject() throws Exception { + return delegate.getSessionFactory(); } - /** - * Subclasses can override this method to perform custom initialization - * of the SessionFactory instance, creating it via the given Configuration - * object that got prepared by this LocalSessionFactoryBean. - *

The default implementation invokes Configuration's buildSessionFactory. - * A custom implementation could prepare the instance in a specific way, - * or use a custom SessionFactoryImpl subclass. - * @param config Configuration prepared by this LocalSessionFactoryBean - * @return the SessionFactory instance - * @throws HibernateException in case of Hibernate initialization errors - * @see org.hibernate.cfg.Configuration#buildSessionFactory - */ - protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { - return config.buildSessionFactory(); + public Class getObjectType() { + return delegate.getObjectType(); } - /** - * Return the Configuration object used to build the SessionFactory. - * Allows access to configuration metadata stored there (rarely needed). - * @throws IllegalStateException if the Configuration object has not been initialized yet - */ - public final Configuration getConfiguration() { - if (this.configuration == null) { - throw new IllegalStateException("Configuration not initialized yet"); - } - return this.configuration; + public void setBeanClassLoader(ClassLoader classLoader) { + delegate.setBeanClassLoader(classLoader); } - /** - * Executes schema update if requested. - * @see #setSchemaUpdate - * @see #updateDatabaseSchema() - */ - @Override - protected void afterSessionFactoryCreation() throws Exception { - if (this.schemaUpdate) { - updateDatabaseSchema(); - } + public boolean isSingleton() { + return delegate.isSingleton(); } - /** - * Allows for schema export on shutdown. - */ - @Override - public void destroy() throws HibernateException { - DataSource dataSource = getDataSource(); - if (dataSource != null) { - // Make given DataSource available for potential SchemaExport, - // which unfortunately reinstantiates a ConnectionProvider. - configTimeDataSourceHolder.set(dataSource); - } - try { - super.destroy(); - } - finally { - if (dataSource != null) { - // Reset DataSource holder. - configTimeDataSourceHolder.remove(); - } - } + public void afterPropertiesSet() throws Exception { + delegate.afterPropertiesSet(); } - - /** - * Execute schema update script, determined by the Configuration object - * used for creating the SessionFactory. A replacement for Hibernate's - * SchemaUpdate class, for automatically executing schema update scripts - * on application startup. Can also be invoked manually. - *

Fetch the LocalSessionFactoryBean itself rather than the exposed - * SessionFactory to be able to invoke this method, e.g. via - * LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");. - *

Uses the SessionFactory that this bean generates for accessing a - * JDBC connection to perform the script. - * @throws DataAccessException in case of script execution errors - * @see #setSchemaUpdate - * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript - * @see org.hibernate.tool.hbm2ddl.SchemaUpdate - */ - public void updateDatabaseSchema() throws DataAccessException { - logger.info("Updating database schema for Hibernate SessionFactory"); - DataSource dataSource = getDataSource(); - if (dataSource != null) { - // Make given DataSource available for the schema update. - configTimeDataSourceHolder.set(dataSource); - } - try { - HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); - hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); - hibernateTemplate.execute( - new HibernateCallback() { - public Object doInHibernate(Session session) throws HibernateException, SQLException { - Connection con = session.connection(); - Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); - DatabaseMetadata metadata = new DatabaseMetadata(con, dialect); - String[] sql = getConfiguration().generateSchemaUpdateScript(dialect, metadata); - executeSchemaScript(con, sql); - return null; - } - } - ); - } - finally { - if (dataSource != null) { - configTimeDataSourceHolder.remove(); - } - } + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + return delegate.translateExceptionIfPossible(ex); } - /** - * Execute schema creation script, determined by the Configuration object - * used for creating the SessionFactory. A replacement for Hibernate's - * SchemaValidator class, to be invoked after application startup. - *

Fetch the LocalSessionFactoryBean itself rather than the exposed - * SessionFactory to be able to invoke this method, e.g. via - * LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");. - *

Uses the SessionFactory that this bean generates for accessing a - * JDBC connection to perform the script. - * @throws DataAccessException in case of script execution errors - * @see org.hibernate.cfg.Configuration#validateSchema - * @see org.hibernate.tool.hbm2ddl.SchemaValidator - */ - public void validateDatabaseSchema() throws DataAccessException { - logger.info("Validating database schema for Hibernate SessionFactory"); - DataSource dataSource = getDataSource(); - if (dataSource != null) { - // Make given DataSource available for the schema update. - configTimeDataSourceHolder.set(dataSource); - } - try { - HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); - hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); - hibernateTemplate.execute( - new HibernateCallback() { - public Object doInHibernate(Session session) throws HibernateException, SQLException { - Connection con = session.connection(); - Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); - DatabaseMetadata metadata = new DatabaseMetadata(con, dialect, false); - getConfiguration().validateSchema(dialect, metadata); - return null; - } - } - ); - } - finally { - if (dataSource != null) { - configTimeDataSourceHolder.remove(); - } - } + public void setJdbcExceptionTranslator( + SQLExceptionTranslator jdbcExceptionTranslator) { + delegate.setJdbcExceptionTranslator(jdbcExceptionTranslator); } - /** - * Execute schema drop script, determined by the Configuration object - * used for creating the SessionFactory. A replacement for Hibernate's - * SchemaExport class, to be invoked on application setup. - *

Fetch the LocalSessionFactoryBean itself rather than the exposed - * SessionFactory to be able to invoke this method, e.g. via - * LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");. - *

Uses the SessionFactory that this bean generates for accessing a - * JDBC connection to perform the script. - * @throws org.springframework.dao.DataAccessException in case of script execution errors - * @see org.hibernate.cfg.Configuration#generateDropSchemaScript - * @see org.hibernate.tool.hbm2ddl.SchemaExport#drop - */ - public void dropDatabaseSchema() throws DataAccessException { - logger.info("Dropping database schema for Hibernate SessionFactory"); - HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); - hibernateTemplate.execute( - new HibernateCallback() { - public Object doInHibernate(Session session) throws HibernateException, SQLException { - Connection con = session.connection(); - Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); - String[] sql = getConfiguration().generateDropSchemaScript(dialect); - executeSchemaScript(con, sql); - return null; - } - } - ); + public void setPersistenceExceptionTranslator( + HibernateExceptionTranslator hibernateExceptionTranslator) { + delegate.setPersistenceExceptionTranslator(hibernateExceptionTranslator); } /** - * Execute schema creation script, determined by the Configuration object - * used for creating the SessionFactory. A replacement for Hibernate's - * SchemaExport class, to be invoked on application setup. - *

Fetch the LocalSessionFactoryBean itself rather than the exposed - * SessionFactory to be able to invoke this method, e.g. via - * LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");. - *

Uses the SessionFactory that this bean generates for accessing a - * JDBC connection to perform the script. - * @throws DataAccessException in case of script execution errors - * @see org.hibernate.cfg.Configuration#generateSchemaCreationScript - * @see org.hibernate.tool.hbm2ddl.SchemaExport#create + * @deprecated as of Spring 3.1 in favor of {@link #newSessionFactory()} which + * can access the internal {@code Configuration} instance via {@link #getConfiguration()}. */ - public void createDatabaseSchema() throws DataAccessException { - logger.info("Creating database schema for Hibernate SessionFactory"); - DataSource dataSource = getDataSource(); - if (dataSource != null) { - // Make given DataSource available for the schema update. - configTimeDataSourceHolder.set(dataSource); - } - try { - HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); - hibernateTemplate.execute( - new HibernateCallback() { - public Object doInHibernate(Session session) throws HibernateException, SQLException { - Connection con = session.connection(); - Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); - String[] sql = getConfiguration().generateSchemaCreationScript(dialect); - executeSchemaScript(con, sql); - return null; - } - } - ); - } - finally { - if (dataSource != null) { - configTimeDataSourceHolder.remove(); - } - } + @Deprecated + protected SessionFactory newSessionFactory(org.hibernate.cfg.Configuration config) throws HibernateException { + return this.newSessionFactory(); } /** - * Execute the given schema script on the given JDBC Connection. - *

Note that the default implementation will log unsuccessful statements - * and continue to execute. Override the executeSchemaStatement - * method to treat failures differently. - * @param con the JDBC Connection to execute the script on - * @param sql the SQL statements to execute - * @throws SQLException if thrown by JDBC methods - * @see #executeSchemaStatement + * @deprecated as of Spring 3.1 in favor of {@link #postProcessMappings()} which + * can access the internal {@code Configuration} instance via {@link #getConfiguration()}. */ - protected void executeSchemaScript(Connection con, String[] sql) throws SQLException { - if (sql != null && sql.length > 0) { - boolean oldAutoCommit = con.getAutoCommit(); - if (!oldAutoCommit) { - con.setAutoCommit(true); - } - try { - Statement stmt = con.createStatement(); - try { - for (String sqlStmt : sql) { - executeSchemaStatement(stmt, sqlStmt); - } - } - finally { - JdbcUtils.closeStatement(stmt); - } - } - finally { - if (!oldAutoCommit) { - con.setAutoCommit(false); - } - } - } + @Deprecated + protected void postProcessMappings(org.hibernate.cfg.Configuration config) throws HibernateException { + this.postProcessMappings(); } /** - * Execute the given schema SQL on the given JDBC Statement. - *

Note that the default implementation will log unsuccessful statements - * and continue to execute. Override this method to treat failures differently. - * @param stmt the JDBC Statement to execute the SQL on - * @param sql the SQL statement to execute - * @throws SQLException if thrown by JDBC methods (and considered fatal) + * @deprecated as of Spring 3.1 in favor of {@link #postProcessConfiguration()} which + * can access the internal {@code Configuration} instance via {@link #getConfiguration()}. */ - protected void executeSchemaStatement(Statement stmt, String sql) throws SQLException { - if (logger.isDebugEnabled()) { - logger.debug("Executing schema statement: " + sql); - } - try { - stmt.executeUpdate(sql); - } - catch (SQLException ex) { - if (logger.isWarnEnabled()) { - logger.warn("Unsuccessful schema statement: " + sql, ex); - } - } + @Deprecated + protected void postProcessConfiguration(org.hibernate.cfg.Configuration config) throws HibernateException { + this.postProcessConfiguration(); } } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalTransactionManagerLookup.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalTransactionManagerLookup.java index 0990f8384fc..f02770f621e 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalTransactionManagerLookup.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalTransactionManagerLookup.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,11 @@ import org.hibernate.transaction.TransactionManagerLookup; /** * Implementation of Hibernate's {@link TransactionManagerLookup} interface * that returns a Spring-managed JTA {@link TransactionManager}, determined - * by LocalSessionFactoryBean's "jtaTransactionManager" property. + * by SessionFactoryBuilder's "jtaTransactionManager" property. * *

The main advantage of this TransactionManagerLookup is that it avoids * double configuration of JTA specifics. A single TransactionManager bean can - * be used for both JtaTransactionManager and LocalSessionFactoryBean, with no + * be used for both JtaTransactionManager and SessionFactoryBuilder, with no * JTA setup in Hibernate configuration. * *

Alternatively, use Hibernate's own TransactionManagerLookup implementations: @@ -40,7 +40,7 @@ import org.hibernate.transaction.TransactionManagerLookup; * * @author Juergen Hoeller * @since 1.2 - * @see LocalSessionFactoryBean#setJtaTransactionManager + * @see SessionFactoryBuilder#setJtaTransactionManager * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager */ public class LocalTransactionManagerLookup implements TransactionManagerLookup { @@ -49,11 +49,11 @@ public class LocalTransactionManagerLookup implements TransactionManagerLookup { public LocalTransactionManagerLookup() { - TransactionManager tm = LocalSessionFactoryBean.getConfigTimeTransactionManager(); + TransactionManager tm = SessionFactoryBuilderSupport.getConfigTimeTransactionManager(); // absolutely needs thread-bound TransactionManager to initialize if (tm == null) { throw new IllegalStateException("No JTA TransactionManager found - " + - "'jtaTransactionManager' property must be set on LocalSessionFactoryBean"); + "'jtaTransactionManager' property must be set on SessionFactoryBuilder"); } this.transactionManager = tm; } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBeanDelegate.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBeanDelegate.java new file mode 100644 index 00000000000..4bf3b613237 --- /dev/null +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBeanDelegate.java @@ -0,0 +1,198 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate3; + +import javax.sql.DataSource; + +import org.hibernate.HibernateException; +import org.hibernate.SessionFactory; +import org.hibernate.cache.RegionFactory; +import org.hibernate.cfg.Environment; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.support.SQLExceptionTranslator; + +/** + * Encapsulates common {@link SessionFactoryBeanOperations} behavior in order + * to avoid multiple-inheritance issues with {@code SessionFactoryBeanOperations} + * implementations that need to extend {@code *SessionFactoryBuilder} types. + * + *

Maintainer's note: Unless otherwise documented, JavaDoc for all methods is + * inherited from {@link SessionFactoryBeanOperations}. + * + * @author Chris Beams + * @since 3.1 + */ +public class SessionFactoryBeanDelegate implements SessionFactoryBeanOperations { + + private SessionFactory sessionFactory; + + private HibernateExceptionTranslator hibernateExceptionTranslator = new HibernateExceptionTranslator(); + + private final SessionFactoryBuilderSupport builder; + + private final ThreadContextClassLoaderHelper threadContextClassLoader = new ThreadContextClassLoaderHelper(); + + @Deprecated + private org.hibernate.cache.CacheProvider cacheProvider; + + @Deprecated + private static final ThreadLocal configTimeCacheProviderHolder = + new ThreadLocal(); + + /** + * Create a new {@code SessionFactoryBeanDelegate} + * @param builder the {@code *SessionFactoryBuilder} that delegates to this + * instance. + */ + public SessionFactoryBeanDelegate(SessionFactoryBuilderSupport builder) { + this.builder = builder; + } + + /** + * Return the {@code SessionFactory} maintained by this delegate. + */ + public SessionFactory getSessionFactory() { + return sessionFactory; + } + + /** + * Return the CacheProvider for the currently configured Hibernate SessionFactory, + * to be used by LocalCacheProviderProxy. + *

This instance will be set before initialization of the corresponding + * SessionFactory, and reset immediately afterwards. It is thus only available + * during configuration. + * @see #setCacheProvider + * @deprecated as of Spring 3.1 in favor of Hibernate's {@link RegionFactory} SPI + */ + @Deprecated + public static org.hibernate.cache.CacheProvider getConfigTimeCacheProvider() { + return configTimeCacheProviderHolder.get(); + } + + @Deprecated + public void setCacheProvider(org.hibernate.cache.CacheProvider cacheProvider) { + this.cacheProvider = cacheProvider; + } + + public void setPersistenceExceptionTranslator(HibernateExceptionTranslator hibernateExceptionTranslator) { + this.hibernateExceptionTranslator = hibernateExceptionTranslator; + } + + public void setBeanClassLoader(ClassLoader beanClassLoader) { + builder.setClassLoader(beanClassLoader); + } + + public SessionFactory getObject() { + return this.sessionFactory; + } + + public Class getObjectType() { + return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class); + } + + public boolean isSingleton() { + return true; + } + + public void afterPropertiesSet() throws Exception { + SessionFactory rawSf = builder.doBuildSessionFactory(); + this.sessionFactory = builder.wrapSessionFactoryIfNecessary(rawSf); + builder.afterSessionFactoryCreation(); + } + + public void destroy() throws HibernateException { + builder.logger.info("Closing Hibernate SessionFactory"); + DataSource dataSource = builder.getDataSource(); + if (dataSource != null) { + // Make given DataSource available for potential SchemaExport, + // which unfortunately reinstantiates a ConnectionProvider. + SessionFactoryBuilderSupport.configTimeDataSourceHolder.set(dataSource); + } + try { + builder.beforeSessionFactoryDestruction(); + } + finally { + this.sessionFactory.close(); + if (dataSource != null) { + // Reset DataSource holder. + SessionFactoryBuilderSupport.configTimeDataSourceHolder.remove(); + } + } + } + + public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) { + this.hibernateExceptionTranslator.setJdbcExceptionTranslator(jdbcExceptionTranslator); + } + + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + return hibernateExceptionTranslator.translateExceptionIfPossible(ex); + } + + /** + * @see SessionFactoryBuilderSupport#preBuildSessionFactory() + */ + @SuppressWarnings("deprecation") + public void preBuildSessionFactory() { + if (this.cacheProvider != null) { + // Make Spring-provided Hibernate CacheProvider available. + configTimeCacheProviderHolder.set(this.cacheProvider); + } + if (builder.getCacheRegionFactory() == null && this.cacheProvider != null) { + // Expose Spring-provided Hibernate CacheProvider. + builder.getConfiguration().setProperty(Environment.CACHE_PROVIDER, LocalCacheProviderProxy.class.getName()); + } + + // Analogous to Hibernate EntityManager's Ejb3Configuration: + // Hibernate doesn't allow setting the bean ClassLoader explicitly, + // so we need to expose it as thread context ClassLoader accordingly. + threadContextClassLoader.overrideIfNecessary(); + } + + /** + * @see SessionFactoryBuilderSupport#postBuildSessionFactory + */ + public void postBuildSessionFactory() { + if (this.cacheProvider != null) { + configTimeCacheProviderHolder.remove(); + } + threadContextClassLoader.resetIfNecessary(); + } + + + private class ThreadContextClassLoaderHelper { + private Thread currentThread = Thread.currentThread(); + private boolean shouldOverride = false; + private ClassLoader originalLoader; + + void overrideIfNecessary() { + this.originalLoader = currentThread.getContextClassLoader(); + this.shouldOverride = + (builder.getBeanClassLoader() != null && !builder.getBeanClassLoader().equals(this.originalLoader)); + if (shouldOverride) { + this.currentThread.setContextClassLoader(builder.getBeanClassLoader()); + } + } + + void resetIfNecessary() { + if (this.shouldOverride) { + // Reset original thread context ClassLoader. + this.currentThread.setContextClassLoader(this.originalLoader); + } + } + } + +} diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBeanOperations.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBeanOperations.java new file mode 100644 index 00000000000..e4c810d8aa1 --- /dev/null +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBeanOperations.java @@ -0,0 +1,128 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate3; + +import org.hibernate.HibernateException; +import org.hibernate.SessionFactory; +import org.hibernate.cache.RegionFactory; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.jdbc.support.SQLExceptionTranslator; +import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean; + +/** + * Operations common to {@link LocalSessionFactoryBean} and + * {@link AnnotationSessionFactoryBean} types but not available via + * their respective {@code *Builder} supertypes. + * + * @author Chris Beams + * @since 3.1 + * @see LocalSessionFactoryBean + * @see AnnotationSessionFactoryBean + * @see SessionFactoryBeanDelegate + */ +public interface SessionFactoryBeanOperations + extends BeanClassLoaderAware, FactoryBean, + InitializingBean, DisposableBean, PersistenceExceptionTranslator { + + /** + * Deprecated. as of Spring 3.0 in favor of {@code setCacheRegionFactory} + * following Hibernate 3.3's deprecation of the {@code CacheProvider} SPI. + *

Set the Hibernate {@code CacheProvider} to use for the {@code SessionFactory}. + * Allows for using a Spring-managed {@code CacheProvider} instance. + *

Note: If this is set, the Hibernate settings should not define a + * cache provider to avoid meaningless double configuration. + * of the {@code CacheProvider} SPI in favor of {@link RegionFactory} SPI. + */ + @Deprecated + void setCacheProvider(org.hibernate.cache.CacheProvider cacheProvider); + + /** + * Customize the {@code HibernateExceptionTranslator} to be used when translating native + * {@code HibernateException} types to Spring's {@code DataAccessException} hierarchy. + */ + void setPersistenceExceptionTranslator(HibernateExceptionTranslator hibernateExceptionTranslator); + + /** + * Exists for compatibility with {@link BeanClassLoaderAware} but + * simply delegates to + * {@link SessionFactoryBuilderSupport#setClassLoader setClassLoader}. + */ + void setBeanClassLoader(ClassLoader beanClassLoader); + + /** + * Return the singleton SessionFactory. + */ + SessionFactory getObject() throws Exception; + + /** + * Return the SessionFactory class used. + */ + Class getObjectType(); + + /** + * Return {@code true}. + */ + boolean isSingleton(); + + /** + * Build and expose the SessionFactory. + * @see SessionFactoryBuilderSupport#buildSessionFactory + * @see SessionFactoryBuilderSupport#doBuildSessionFactory + * @see SessionFactoryBuilderSupport#wrapSessionFactoryIfNecessary + * @see SessionFactoryBuilderSupport#afterSessionFactoryCreation + */ + void afterPropertiesSet() throws Exception; + + /** + * Close the SessionFactory on bean factory shutdown. + */ + void destroy() throws HibernateException; + + /** + * Set the JDBC exception translator for the SessionFactory + * on this instance's underlying {@link #setPersistenceExceptionTranslator + * HibernateExceptionTranslator}. + *

Applied to any SQLException root cause of a Hibernate JDBCException, + * overriding Hibernate's default SQLException translation (which is + * based on Hibernate's Dialect for a specific target database). + * @param jdbcExceptionTranslator the exception translator + * @see #setPersistenceExceptionTranslator(HibernateExceptionTranslator) + * @see HibernateExceptionTranslator#setJdbcExceptionTranslator + * @see java.sql.SQLException + * @see org.hibernate.JDBCException + * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator + * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator + * @see org.springframework.dao.support.PersistenceExceptionTranslator + */ + void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator); + + /** + * Implementation of the PersistenceExceptionTranslator interface, + * as autodetected by Spring's PersistenceExceptionTranslationPostProcessor. + *

Converts the exception if it is a HibernateException; + * else returns null to indicate an unknown exception. + * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor + * @see #convertHibernateAccessException + */ + DataAccessException translateExceptionIfPossible(RuntimeException ex); + +} diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBuilder.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBuilder.java new file mode 100644 index 00000000000..bcd7e82d56b --- /dev/null +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBuilder.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate3; + +import javax.sql.DataSource; + +import org.hibernate.cfg.Configuration; +import org.springframework.context.annotation.Bean; + +/** + * Hibernate {@link Configuration} builder suitable for use within Spring + * {@link org.springframework.context.annotation.Configuration @Configuration} + * class {@link Bean @Bean} methods. For complete details on features, see the + * JavaDoc for the {@link SessionFactoryBuilderSupport} superclass. For use in + * Spring XML configuration, see the {@link LocalSessionFactoryBean} subclass. + * + *

As noted in {@code SessionFactoryBuilderSupport} JavaDoc, this class requires + * Hibernate 3.2 or later; it additionally requires that the Java Persistence API + * and Hibernate Annotations add-ons are present. + * + *

Setter methods return the builder instance in order to facilitate + * a concise and convenient method-chaining style. For example: + *

+ * {@code @Configuration}
+ * public class DataConfig {
+ *     {@code @Bean}
+ *     public SessionFactory sessionFactory() {
+ *         return new SessionFactoryBean()
+ *             .setDataSource(dataSource())
+ *             .setMappingLocations("classpath:com/myco/*.hbm.xml"})
+ *             .buildSessionFactory();
+ *     }
+ * }
+ * 
+ * + *

Most Hibernate configuration operations can be performed directly against + * this API; however you may also access access and configure the underlying + * {@code Configuration} object by using the {@link #doWithConfiguration} method + * and providing a {@code HibernateConfigurationCallback} as follows: + *

+ * SessionFactory sessionFactory =
+ *     new SessionFactoryBuilder()
+ *         // ...
+ *         .doWithConfiguration(new HibernateConfigurationCallback<Configuration>() {
+ *             public void configure(Configuration cfg) {
+ *                 cfg.setNamingStrategy(MyNamingStrategy.class);
+ *             }
+ *          })
+ *         .buildSessionFactory();
+ * 
+ * + * @author Juergen Hoeller + * @author Chris Beams + * @since 3.1 + * @see SessionFactoryBuilderSupport + * @see LocalSessionFactoryBean + * @see org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBuilder + * @see org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean + */ +public class SessionFactoryBuilder extends SessionFactoryBuilderSupport { + + /** + * Construct a new {@code SessionFactoryBuilder} + */ + public SessionFactoryBuilder() { + super(); + } + + /** + * Construct a new {@code SessionFactoryBuilder} with the given + * Spring-managed {@code DataSource} instance. + * @see #setDataSource + */ + public SessionFactoryBuilder(DataSource dataSource) { + super(dataSource); + } + + /** + * {@inheritDoc} + *

This implementation returns {@code org.hibernate.cfg.Configuration} + */ + @Override + protected Class getDefaultConfigurationClass() { + return Configuration.class; + } + +} diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBuilderSupport.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBuilderSupport.java new file mode 100644 index 00000000000..c7c82d394c4 --- /dev/null +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryBuilderSupport.java @@ -0,0 +1,1408 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate3; + +import java.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Map; +import java.util.Properties; + +import javax.sql.DataSource; +import javax.transaction.TransactionManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.HibernateException; +import org.hibernate.Interceptor; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.cfg.NamingStrategy; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.FilterDefinition; +import org.hibernate.engine.SessionFactoryImplementor; +import org.hibernate.event.EventListeners; +import org.hibernate.tool.hbm2ddl.DatabaseMetadata; +import org.hibernate.transaction.JTATransactionFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; +import org.springframework.jdbc.support.JdbcUtils; +import org.springframework.jdbc.support.lob.LobHandler; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +/** + * Abstract base class for Spring's Hibernate {@code SessionFactory} {@code + * *Builder} and {@code *FactoryBean} types. Provides functionality suitable for + * setting up a shared Hibernate {@code SessionFactory} in a Spring application + * context; the {@code SessionFactory} can then be passed to Hibernate-based + * Repositories/DAOs via dependency injection. + * + *

Configuration settings can either be read from a + * Hibernate XML file, specified by the + * {@linkplain #setConfigLocation configLocation} property, or completely via this + * class. A typical local configuration consists of one or more {@linkplain + * #setMappingResources mappingResources}, various {@linkplain + * #setHibernateProperties hibernateProperties} (not strictly necessary), and a + * {@linkplain #setDataSource dataSource} that the {@code SessionFactory} should + * use. The latter can also be specified via Hibernate properties, but {@link + * #setDataSource} supports any Spring-configured {@code DataSource}, instead of + * relying on Hibernate's own connection providers. + * + *

This {@code SessionFactory} handling strategy is appropriate for most types + * of applications, from Hibernate-only single database apps to ones that need + * distributed transactions. Either a {@link HibernateTransactionManager} or {@link + * org.springframework.transaction.jta.JtaTransactionManager JtaTransactionManager} + * can be used for transaction demarcation, with the latter only necessary for + * transactions which span multiple databases. + * + *

By default a transaction-aware {@code SessionFactory} proxy will be exposed, + * letting data access code work with the plain Hibernate {@code SessionFactory} + * and its {@code getCurrentSession()} method, while still being able to + * participate in current Spring-managed transactions: with any transaction + * management strategy, either local or JTA / EJB CMT, and any transaction + * synchronization mechanism, either Spring or JTA. Furthermore, + * {@code getCurrentSession()} will also seamlessly work with a request-scoped + * {@code Session} managed by + * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter} or + * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}. + * + *

Requires Hibernate 3.2 or later; tested against 3.2, 3.3, 3.5 and 3.6. + * Note that this factory will use "on_close" as default Hibernate connection + * release mode, unless in the case of a {@linkplain #setJtaTransactionManager + * jtaTransactionManager} being specified, as this is appropriate for most + * Spring-based applications (in particular when using Spring's + * {@code HibernateTransactionManager}). + * + *

Concrete {@code *Builder} subclasses are designed for use within Spring + * {@link org.springframework.context.annotation.Configuration @Configuration} + * classes, while {@code *FactoryBean} subclasses are designed for use when + * configuring the container via Spring XML. See individual subclass documentation + * for complete details. + * + *

It is common to use the subclasses mentioned above in conjunction with + * Spring's {@link HibernateExceptionTranslator} and a + * {@code PersistenceExceptionTranslationPostProcessor} in order to convert native + * {@code HibernateException} types to Spring's {@link DataAccessException} + * hierarchy. {@code *FactoryBean} automatically perform translation through a + * built-in {@code HibernateExceptionTranslator}, but {@code @Configuration} + * class {@code @Bean} methods that use {@code *Builder} subclasses should + * manually register a {@code HibernateExceptionTranslator} {@code @Bean}. + * When using either type of subclass you must manually register a + * {@code PersistenceExceptionTranslationPostProcessor} bean. + * + * @author Juergen Hoeller + * @author Chris Beams + * @since 3.1 + * @see SessionFactoryBuilder + * @see LocalSessionFactoryBean + * @see org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBuilder + * @see org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean + * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor + * @see org.springframework.orm.hibernate3.HibernateExceptionTranslator + * @see HibernateTemplate#setSessionFactory + * @see HibernateTransactionManager#setSessionFactory + * @see #setExposeTransactionAwareSessionFactory + * @see #setJtaTransactionManager + * @see org.hibernate.SessionFactory#getCurrentSession + * @see HibernateTransactionManager + */ +public abstract class SessionFactoryBuilderSupport> { + + static final ThreadLocal configTimeDataSourceHolder = + new ThreadLocal(); + + private static final ThreadLocal configTimeTransactionManagerHolder = + new ThreadLocal(); + + private static final ThreadLocal configTimeRegionFactoryHolder = + new ThreadLocal(); + + private static final ThreadLocal configTimeLobHandlerHolder = + new ThreadLocal(); + + /** Logger available to subclasses */ + protected final Log logger = LogFactory.getLog(getClass()); + + @SuppressWarnings("unchecked") + private final This instance = (This) this; + + private Class configurationClass = getDefaultConfigurationClass(); + + /** Lazily initialized during calls to {@link #getConfiguration()} */ + private Configuration configuration; + + private DataSource dataSource; + + private SessionFactory sessionFactory; + + private boolean schemaUpdate = false; + + private boolean exposeTransactionAwareSessionFactory = true; + + private boolean useTransactionAwareDataSource = false; + + private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); + + private Resource[] mappingLocations; + + private Resource[] cacheableMappingLocations; + + private Resource[] mappingJarLocations; + + private Resource[] mappingDirectoryLocations; + + private TransactionManager jtaTransactionManager; + + private Object cacheRegionFactory; + + private LobHandler lobHandler; + + private Interceptor entityInterceptor; + + private NamingStrategy namingStrategy; + + private TypeDefinitionBean[] typeDefinitions; + + private FilterDefinition[] filterDefinitions; + + private Resource[] configLocations; + + private Properties hibernateProperties; + + private Properties entityCacheStrategies; + + private Properties collectionCacheStrategies; + + private Map eventListeners; + + private String[] mappingResources; + + /** + * Constructor for use when DataSource is not available as a Spring bean + * instance, i.e.: when DataSource configuration should be read from + * {@code hibernate.cfg.xml} + * @see SessionFactoryBuilderSupport#AbstractSessionFactoryBuilder(DataSource) + */ + public SessionFactoryBuilderSupport() { + } + + public SessionFactoryBuilderSupport(DataSource dataSource) { + this.dataSource = dataSource; + } + + /** + * Return the default Configuration type used by this instance. + * May be overridden with {@link #setConfigurationClass(Class)}. + * @see #setConfigurationClass(Class) + */ + protected abstract Class getDefaultConfigurationClass(); + + /** + * Build the underlying Hibernate SessionFactory. + * @return the raw SessionFactory (potentially to be wrapped with a + * transaction-aware proxy before it is exposed to the application) + * @throws Exception in case of initialization failure + */ + public SessionFactory buildSessionFactory() throws Exception { + this.sessionFactory = wrapSessionFactoryIfNecessary(doBuildSessionFactory()); + afterSessionFactoryCreation(); + return this.sessionFactory; + } + + /** + * Populate the underlying {@code Configuration} instance with the various + * properties of this builder. Customization may be performed through + * {@code pre*} and {@code post*} methods. + * @see #preBuildSessionFactory() + * @see #postProcessMappings() + * @see #postBuildSessionFactory() + * @see #postBuildSessionFactory() + */ + protected final SessionFactory doBuildSessionFactory() throws Exception { + initializeConfigurationIfNecessary(); + + if (this.dataSource != null) { + // Make given DataSource available for SessionFactory configuration. + configTimeDataSourceHolder.set(this.dataSource); + } + if (this.jtaTransactionManager != null) { + // Make Spring-provided JTA TransactionManager available. + configTimeTransactionManagerHolder.set(this.jtaTransactionManager); + } + if (this.cacheRegionFactory != null) { + // Make Spring-provided Hibernate RegionFactory available. + configTimeRegionFactoryHolder.set(this.cacheRegionFactory); + } + if (this.lobHandler != null) { + // Make given LobHandler available for SessionFactory configuration. + // Do early because because mapping resource might refer to custom types. + configTimeLobHandlerHolder.set(this.lobHandler); + } + + try { + if (isExposeTransactionAwareSessionFactory()) { + // Set Hibernate 3.1+ CurrentSessionContext implementation, + // providing the Spring-managed Session as current Session. + // Can be overridden by a custom value for the corresponding Hibernate property. + this.configuration.setProperty( + Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName()); + } + + if (this.jtaTransactionManager != null) { + // Set Spring-provided JTA TransactionManager as Hibernate property. + this.configuration.setProperty( + Environment.TRANSACTION_STRATEGY, JTATransactionFactory.class.getName()); + this.configuration.setProperty( + Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName()); + } + else { + // Makes the Hibernate Session aware of the presence of a Spring-managed transaction. + // Also sets connection release mode to ON_CLOSE by default. + this.configuration.setProperty( + Environment.TRANSACTION_STRATEGY, SpringTransactionFactory.class.getName()); + } + + if (this.entityInterceptor != null) { + // Set given entity interceptor at SessionFactory level. + this.configuration.setInterceptor(this.entityInterceptor); + } + + if (this.namingStrategy != null) { + // Pass given naming strategy to Hibernate Configuration. + this.configuration.setNamingStrategy(this.namingStrategy); + } + + if (this.typeDefinitions != null) { + // Register specified Hibernate type definitions. + // Use reflection for compatibility with both Hibernate 3.3 and 3.5: + // the returned Mappings object changed from a class to an interface. + Method createMappings = Configuration.class.getMethod("createMappings"); + Method addTypeDef = createMappings.getReturnType().getMethod( + "addTypeDef", String.class, String.class, Properties.class); + Object mappings = ReflectionUtils.invokeMethod(createMappings, this.configuration); + for (TypeDefinitionBean typeDef : this.typeDefinitions) { + ReflectionUtils.invokeMethod(addTypeDef, mappings, + typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters()); + } + } + + if (this.filterDefinitions != null) { + // Register specified Hibernate FilterDefinitions. + for (FilterDefinition filterDef : this.filterDefinitions) { + this.configuration.addFilterDefinition(filterDef); + } + } + + if (this.configLocations != null) { + for (Resource resource : this.configLocations) { + // Load Hibernate configuration from given location. + this.configuration.configure(resource.getURL()); + } + } + + if (this.hibernateProperties != null) { + // Add given Hibernate properties to Configuration. + this.configuration.addProperties(this.hibernateProperties); + } + + if (dataSource != null) { + Class providerClass = LocalDataSourceConnectionProvider.class; + if (isUseTransactionAwareDataSource() || dataSource instanceof TransactionAwareDataSourceProxy) { + providerClass = TransactionAwareDataSourceConnectionProvider.class; + } + else if (this.configuration.getProperty(Environment.TRANSACTION_MANAGER_STRATEGY) != null) { + providerClass = LocalJtaDataSourceConnectionProvider.class; + } + // Set Spring-provided DataSource as Hibernate ConnectionProvider. + this.configuration.setProperty(Environment.CONNECTION_PROVIDER, providerClass.getName()); + } + + if (this.cacheRegionFactory != null) { + // Expose Spring-provided Hibernate RegionFactory. + this.configuration.setProperty(Environment.CACHE_REGION_FACTORY, + "org.springframework.orm.hibernate3.LocalRegionFactoryProxy"); + } + + if (this.mappingResources != null) { + // Register given Hibernate mapping definitions, contained in resource files. + for (String mapping : this.mappingResources) { + Resource resource = new ClassPathResource(mapping.trim(), this.beanClassLoader); + this.configuration.addInputStream(resource.getInputStream()); + } + } + + if (this.mappingLocations != null) { + // Register given Hibernate mapping definitions, contained in resource files. + for (Resource resource : this.mappingLocations) { + this.configuration.addInputStream(resource.getInputStream()); + } + } + + if (this.cacheableMappingLocations != null) { + // Register given cacheable Hibernate mapping definitions, read from the file system. + for (Resource resource : this.cacheableMappingLocations) { + this.configuration.addCacheableFile(resource.getFile()); + } + } + + if (this.mappingJarLocations != null) { + // Register given Hibernate mapping definitions, contained in jar files. + for (Resource resource : this.mappingJarLocations) { + this.configuration.addJar(resource.getFile()); + } + } + + if (this.mappingDirectoryLocations != null) { + // Register all Hibernate mapping definitions in the given directories. + for (Resource resource : this.mappingDirectoryLocations) { + File file = resource.getFile(); + if (!file.isDirectory()) { + throw new IllegalArgumentException( + "Mapping directory location [" + resource + "] does not denote a directory"); + } + this.configuration.addDirectory(file); + } + } + + // Tell Hibernate to eagerly compile the mappings that we registered, + // for availability of the mapping information in further processing. + postProcessMappings(); + this.configuration.buildMappings(); + + if (this.entityCacheStrategies != null) { + // Register cache strategies for mapped entities. + for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) { + String className = (String) classNames.nextElement(); + String[] strategyAndRegion = + StringUtils.commaDelimitedListToStringArray(this.entityCacheStrategies.getProperty(className)); + if (strategyAndRegion.length > 1) { + // method signature declares return type as Configuration on Hibernate 3.6 + // but as void on Hibernate 3.3 and 3.5 + Method setCacheConcurrencyStrategy = Configuration.class.getMethod( + "setCacheConcurrencyStrategy", String.class, String.class, String.class); + ReflectionUtils.invokeMethod(setCacheConcurrencyStrategy, this.configuration, + className, strategyAndRegion[0], strategyAndRegion[1]); + } + else if (strategyAndRegion.length > 0) { + this.configuration.setCacheConcurrencyStrategy(className, strategyAndRegion[0]); + } + } + } + + if (this.collectionCacheStrategies != null) { + // Register cache strategies for mapped collections. + for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) { + String collRole = (String) collRoles.nextElement(); + String[] strategyAndRegion = + StringUtils.commaDelimitedListToStringArray(this.collectionCacheStrategies.getProperty(collRole)); + if (strategyAndRegion.length > 1) { + this.configuration.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0], strategyAndRegion[1]); + } + else if (strategyAndRegion.length > 0) { + this.configuration.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]); + } + } + } + + if (this.eventListeners != null) { + // Register specified Hibernate event listeners. + for (Map.Entry entry : this.eventListeners.entrySet()) { + String listenerType = entry.getKey(); + Object listenerObject = entry.getValue(); + if (listenerObject instanceof Collection) { + Collection listeners = (Collection) listenerObject; + EventListeners listenerRegistry = this.configuration.getEventListeners(); + Object[] listenerArray = + (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size()); + listenerArray = listeners.toArray(listenerArray); + this.configuration.setListeners(listenerType, listenerArray); + } + else { + this.configuration.setListener(listenerType, listenerObject); + } + } + } + + preBuildSessionFactory(); + + // Perform custom post-processing in subclasses. + postProcessConfiguration(); + + // Build SessionFactory instance. + logger.info("Building new Hibernate SessionFactory"); + + return this.sessionFactory = newSessionFactory(); + } + finally { + if (dataSource != null) { + SessionFactoryBuilderSupport.configTimeDataSourceHolder.remove(); + } + if (this.jtaTransactionManager != null) { + SessionFactoryBuilderSupport.configTimeTransactionManagerHolder.remove(); + } + if (this.cacheRegionFactory != null) { + SessionFactoryBuilderSupport.configTimeRegionFactoryHolder.remove(); + } + if (this.lobHandler != null) { + SessionFactoryBuilderSupport.configTimeLobHandlerHolder.remove(); + } + + postBuildSessionFactory(); + } + } + + /** + * Return the Hibernate {@link Configuration} object used to build the {@link SessionFactory}. + * Allows access to configuration metadata stored there (rarely needed). + *

Favor use of {@link #doWithConfiguration(HibernateConfigurationCallback)} + * in order to customize the internal {@code Configuration} object. + * @see #doWithConfiguration(HibernateConfigurationCallback) + * @throws IllegalStateException if the Configuration object has not been initialized yet + */ + protected final Configuration getConfiguration() { + initializeConfigurationIfNecessary(); + return this.configuration; + } + + private void initializeConfigurationIfNecessary() { + if (this.configuration == null) { + this.configuration = newConfiguration(); + } + } + + /** + * Subclasses can override this method to perform custom initialization + * of the Configuration instance used for SessionFactory creation. + * The properties of this Builder will be applied to + * the Configuration object that gets returned here. + *

The default implementation creates a new Configuration instance. + * A custom implementation could prepare the instance in a specific way, + * or use a custom Configuration subclass. + * @return the Configuration instance + * @throws HibernateException in case of Hibernate initialization errors + * @see org.hibernate.cfg.Configuration#Configuration() + */ + /** + * Instantiate and return an instance of the {@link Configuration} class + * type for this builder. Subclasses may override in order to customize + * instantiation logic, but clients should use {@link #doWithConfiguration} + * to directly customize the {@code Configuration} instance. + * @see #getDefaultConfigurationClass() + * @see #setConfigurationClass(Class) + * @see #getConfiguration() + * @see #doWithConfiguration(HibernateConfigurationCallback) + */ + protected Configuration newConfiguration() { + return BeanUtils.instantiateClass(this.configurationClass); + } + + /** + * Return the exposed SessionFactory. + * Will throw an exception if not initialized yet. + * @return the SessionFactory (never null) + * @throws IllegalStateException if the SessionFactory has not been initialized yet + */ + protected final SessionFactory getSessionFactory() { + if (this.sessionFactory == null) { + throw new IllegalStateException("SessionFactory not initialized yet. Have you called buildSessionFactory()?"); + } + return this.sessionFactory; + } + + /** + * Allow additional population of the underlying {@code Configuration} + * instance. Called during {@link #doBuildSessionFactory()}. + */ + protected void preBuildSessionFactory() { + } + + /** + * Allow cleaning up resources, thread locals, etc after building the + * {@code SessionFactory}. Called during the {@code finally} block of + * {@link #doBuildSessionFactory()}. + */ + protected void postBuildSessionFactory() { + } + + /** + * Wrap the given SessionFactory with a proxy, if demanded. + *

The default implementation simply returns the given SessionFactory as-is. + * Subclasses may override this to implement transaction awareness through + * a SessionFactory proxy, for example. + * @param rawSf the raw SessionFactory as built by {@link #buildSessionFactory()} + * @return the SessionFactory reference to expose + * @see #buildSessionFactory() + */ + protected SessionFactory wrapSessionFactoryIfNecessary(SessionFactory rawSf) { + return rawSf; + } + + /** + * Subclasses can override this method to perform custom initialization + * of the SessionFactory instance, creating it via the given Configuration + * object that got prepared by this Builder. + *

The default implementation invokes Configuration's buildSessionFactory. + * A custom implementation could prepare the instance in a specific way, + * or use a custom SessionFactoryImpl subclass. + * @param config Configuration prepared by this Builder + * @return the SessionFactory instance + * @throws HibernateException in case of Hibernate initialization errors + * @see org.hibernate.cfg.Configuration#buildSessionFactory + */ + protected SessionFactory newSessionFactory() throws HibernateException { + return this.configuration.buildSessionFactory(); + } + + /** + * To be implemented by subclasses that want to to register further mappings + * on the Configuration object after this FactoryBean registered its specified + * mappings. + *

Invoked before the Configuration.buildMappings() call, + * so that it can still extend and modify the mapping information. + * @param config the current Configuration object + * @throws HibernateException in case of Hibernate initialization errors + * @see org.hibernate.cfg.Configuration#buildMappings() + */ + protected void postProcessMappings() throws HibernateException { + } + + /** + * To be implemented by subclasses that want to to perform custom + * post-processing of the Configuration object after this FactoryBean + * performed its default initialization. + *

Invoked after the Configuration.buildMappings() call, + * so that it can operate on the completed and fully parsed mapping information. + * @throws HibernateException in case of Hibernate initialization errors + * @see org.hibernate.cfg.Configuration#buildMappings() + */ + protected void postProcessConfiguration() throws HibernateException { + } + + /** + * Hook that allows post-processing after the SessionFactory has been + * successfully created. The SessionFactory is already available through + * getSessionFactory() at this point. + *

This implementation is empty. + * @throws Exception in case of initialization failure + * @see #getSessionFactory() + */ + /** + * Executes schema update if requested. + * @see #setSchemaUpdate + * @see #updateDatabaseSchema() + */ + protected void afterSessionFactoryCreation() throws Exception { + if (this.schemaUpdate) { + updateDatabaseSchema(); + } + } + + /** + * Hook that allows shutdown processing before the SessionFactory + * will be closed. The SessionFactory is still available through + * getSessionFactory() at this point. + *

This implementation is empty. + * @see #getSessionFactory() + */ + protected void beforeSessionFactoryDestruction() { + } + + /** + * Execute schema creation script, determined by the Configuration object + * used for creating the SessionFactory. A replacement for Hibernate's + * SchemaExport class, to be invoked on application setup. + *

Fetch the FactoryBean itself by rather than the exposed + * SessionFactory to be able to invoke this method, e.g. via + *

+	 * LocalSessionFactoryBean sfb = ctx.getBean("&mySessionFactory", LocalSessionFactoryBean.class);
+	 * 
+ * or in the case of {@code @Configuration} class usage, register the + * SessionFactoryBuilder as a {@code @Bean} and fetch it by type: + *
+	 * SessionFactoryBuilder sfb = ctx.getBean(SessionFactoryBuilder.class);
+	 * 
+ *

Uses the SessionFactory that this bean generates for accessing a + * JDBC connection to perform the script. + * @throws DataAccessException in case of script execution errors + * @see org.hibernate.cfg.Configuration#generateSchemaCreationScript + * @see org.hibernate.tool.hbm2ddl.SchemaExport#create + */ + public void createDatabaseSchema() throws DataAccessException { + logger.info("Creating database schema for Hibernate SessionFactory"); + DataSource dataSource = getDataSource(); + if (dataSource != null) { + // Make given DataSource available for the schema update. + SessionFactoryBuilderSupport.configTimeDataSourceHolder.set(dataSource); + } + try { + HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); + hibernateTemplate.execute( + new HibernateCallback() { + @SuppressWarnings("deprecation") + public Object doInHibernate(Session session) throws HibernateException, SQLException { + Dialect dialect = ((SessionFactoryImplementor)session.getSessionFactory()).getDialect(); + String[] sql = configuration.generateSchemaCreationScript(dialect); + executeSchemaScript(session.connection(), sql); + return null; + } + } + ); + } + finally { + if (dataSource != null) { + SessionFactoryBuilderSupport.configTimeDataSourceHolder.remove(); + } + } + } + + /** + * Execute schema update script, determined by the Configuration object + * used for creating the SessionFactory. A replacement for Hibernate's + * SchemaUpdate class, for automatically executing schema update scripts + * on application startup. Can also be invoked manually. + *

Fetch the FactoryBean itself by rather than the exposed + * SessionFactory to be able to invoke this method, e.g. via + *

+	 * LocalSessionFactoryBean sfb = ctx.getBean("&mySessionFactory", LocalSessionFactoryBean.class);
+	 * 
+ * or in the case of {@code @Configuration} class usage, register the + * SessionFactoryBuilder as a {@code @Bean} and fetch it by type: + *
+	 * SessionFactoryBuilder sfb = ctx.getBean(SessionFactoryBuilder.class);
+	 * 
+ *

Uses the SessionFactory that this bean generates for accessing a + * JDBC connection to perform the script. + * @throws DataAccessException in case of script execution errors + * @see #setSchemaUpdate + * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript + * @see org.hibernate.tool.hbm2ddl.SchemaUpdate + */ + public void updateDatabaseSchema() throws DataAccessException { + logger.info("Updating database schema for Hibernate SessionFactory"); + if (this.dataSource != null) { + // Make given DataSource available for the schema update. + configTimeDataSourceHolder.set(this.dataSource); + } + try { + HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); + hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); + hibernateTemplate.execute( + new HibernateCallback() { + public Void doInHibernate(Session session) throws HibernateException, SQLException { + @SuppressWarnings("deprecation") + Connection conn = session.connection(); + Dialect dialect = ((SessionFactoryImplementor)session.getSessionFactory()).getDialect(); + DatabaseMetadata metadata = new DatabaseMetadata(conn, dialect); + String[] sql = configuration.generateSchemaUpdateScript(dialect, metadata); + executeSchemaScript(conn, sql); + return null; + } + } + ); + } + finally { + if (dataSource != null) { + configTimeDataSourceHolder.remove(); + } + } + } + + /** + * Execute schema creation script, determined by the Configuration object + * used for creating the SessionFactory. A replacement for Hibernate's + * SchemaValidator class, to be invoked after application startup. + *

Fetch the FactoryBean itself by rather than the exposed + * SessionFactory to be able to invoke this method, e.g. via + *

+	 * LocalSessionFactoryBean sfb = ctx.getBean("&mySessionFactory", LocalSessionFactoryBean.class);
+	 * 
+ * or in the case of {@code @Configuration} class usage, register the + * SessionFactoryBuilder as a {@code @Bean} and fetch it by type: + *
+	 * SessionFactoryBuilder sfb = ctx.getBean(SessionFactoryBuilder.class);
+	 * 
+ *

Uses the SessionFactory that this bean generates for accessing a + * JDBC connection to perform the script. + * @throws DataAccessException in case of script execution errors + * @see org.hibernate.cfg.Configuration#validateSchema + * @see org.hibernate.tool.hbm2ddl.SchemaValidator + */ + public void validateDatabaseSchema() throws DataAccessException { + logger.info("Validating database schema for Hibernate SessionFactory"); + DataSource dataSource = getDataSource(); + if (dataSource != null) { + // Make given DataSource available for the schema update. + SessionFactoryBuilderSupport.configTimeDataSourceHolder.set(dataSource); + } + try { + HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); + hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); + hibernateTemplate.execute( + new HibernateCallback() { + @SuppressWarnings("deprecation") + public Object doInHibernate(Session session) throws HibernateException, SQLException { + Dialect dialect = ((SessionFactoryImplementor)session.getSessionFactory()).getDialect(); + DatabaseMetadata metadata = new DatabaseMetadata(session.connection(), dialect); + configuration.validateSchema(dialect, metadata); + return null; + } + } + ); + } + finally { + if (dataSource != null) { + SessionFactoryBuilderSupport.configTimeDataSourceHolder.remove(); + } + } + } + + /** + * Execute schema drop script, determined by the Configuration object + * used for creating the SessionFactory. A replacement for Hibernate's + * SchemaExport class, to be invoked on application setup. + *

Fetch the FactoryBean itself by rather than the exposed + * SessionFactory to be able to invoke this method, e.g. via + *

+	 * LocalSessionFactoryBean sfb = ctx.getBean("&mySessionFactory", LocalSessionFactoryBean.class);
+	 * 
+ * or in the case of {@code @Configuration} class usage, register the + * SessionFactoryBuilder as a {@code @Bean} and fetch it by type: + *
+	 * SessionFactoryBuilder sfb = ctx.getBean(SessionFactoryBuilder.class);
+	 * 
+ *

Uses the SessionFactory that this bean generates for accessing a + * JDBC connection to perform the script. + * @throws org.springframework.dao.DataAccessException in case of script execution errors + * @see org.hibernate.cfg.Configuration#generateDropSchemaScript + * @see org.hibernate.tool.hbm2ddl.SchemaExport#drop + */ + public void dropDatabaseSchema() throws DataAccessException { + logger.info("Dropping database schema for Hibernate SessionFactory"); + HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); + hibernateTemplate.execute( + new HibernateCallback() { + @SuppressWarnings("deprecation") + public Void doInHibernate(Session session) throws HibernateException, SQLException { + Dialect dialect = ((SessionFactoryImplementor)session.getSessionFactory()).getDialect(); + String[] sql = configuration.generateDropSchemaScript(dialect); + executeSchemaScript(session.connection(), sql); + return null; + } + } + ); + } + + /** + * Execute the given schema script on the given JDBC Connection. + *

Note that the default implementation will log unsuccessful statements + * and continue to execute. Override the executeSchemaStatement + * method to treat failures differently. + * @param con the JDBC Connection to execute the script on + * @param sql the SQL statements to execute + * @throws SQLException if thrown by JDBC methods + * @see #executeSchemaStatement + */ + protected void executeSchemaScript(Connection con, String[] sql) throws SQLException { + if (sql != null && sql.length > 0) { + boolean oldAutoCommit = con.getAutoCommit(); + if (!oldAutoCommit) { + con.setAutoCommit(true); + } + try { + Statement stmt = con.createStatement(); + try { + for (String sqlStmt : sql) { + executeSchemaStatement(stmt, sqlStmt); + } + } + finally { + JdbcUtils.closeStatement(stmt); + } + } + finally { + if (!oldAutoCommit) { + con.setAutoCommit(false); + } + } + } + } + + /** + * Execute the given schema SQL on the given JDBC Statement. + *

Note that the default implementation will log unsuccessful statements + * and continue to execute. Override this method to treat failures differently. + * @param stmt the JDBC Statement to execute the SQL on + * @param sql the SQL statement to execute + * @throws SQLException if thrown by JDBC methods (and considered fatal) + */ + protected void executeSchemaStatement(Statement stmt, String sql) throws SQLException { + if (logger.isDebugEnabled()) { + logger.debug("Executing schema statement: " + sql); + } + try { + stmt.executeUpdate(sql); + } + catch (SQLException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Unsuccessful schema statement: " + sql, ex); + } + } + } + + /** + * Specify the Hibernate Configuration class to use. + * Default is {@code org.hibernate.cfg.Configuration}; any subclass of + * this default Hibernate {@code Configuration} class can be specified. + *

Can be set to {@code org.hibernate.cfg.AnnotationConfiguration} for + * using Hibernate3 annotation support (initially only available as + * alpha download separate from the main Hibernate3 distribution). + *

Annotated packages and annotated classes can be specified via the + * corresponding tags in "hibernate.cfg.xml" then, so this will usually + * be combined with a "configLocation" property that points at such a + * standard Hibernate configuration file. + * @see #setConfigLocation + * @see #doWithConfiguration + * @see org.hibernate.cfg.Configuration + * @see org.hibernate.cfg.AnnotationConfiguration + */ + public final This setConfigurationClass(Class configurationClass) { + Assert.notNull(configurationClass, "configurationClass must not be null"); + if (this.configuration != null) { + throw new IllegalStateException( + "setConfigurationClass() must be called before the internal " + + "Configuration instance has been created. Have you perhaps called " + + "getConfiguration() or doWithConfiguration() earlier than your call " + + "to setConfiguration()?"); + } + this.configurationClass = configurationClass; + return this.instance; + } + + /** + * Use the given {@link HibernateConfigurationCallback} instance to configure + * this {@code Builder}'s underlying Hibernate {@code Configuration} object. + *

The {@code HibernateConfigurationCallback} type may be parameterized to + * {@code org.hibernate.cfg.Configuration} (the default), or {@code + * org.hibernate.cfg.AnnotationConfiguration} if running a Hibernate version + * less than 3.6. Otherwise, may be parameterized to any custom {@code Configuration} + * class provided to {@link #setConfigurationClass} + * @throws Exception propagating any exception thrown during operations against + * the underlying {@code Configuration} object. {@code @Bean} methods should + * declare {@code throws Exception} when using this method, allowing the Spring + * container to deal with it and fail appropriately. + * @see #setConfigurationClass(Class) + */ + @SuppressWarnings("unchecked") + public This doWithConfiguration(HibernateConfigurationCallback callback) + throws Exception { + callback.configure((C)this.getConfiguration()); + return this.instance; + } + + /** + * Set the DataSource to be used by the SessionFactory. + * If set, this will override corresponding settings in Hibernate properties. + *

If this is set, the Hibernate settings should not define + * a connection provider to avoid meaningless double configuration. + *

If using HibernateTransactionManager as transaction strategy, consider + * proxying your target DataSource with a LazyConnectionDataSourceProxy. + * This defers fetching of an actual JDBC Connection until the first JDBC + * Statement gets executed, even within JDBC transactions (as performed by + * HibernateTransactionManager). Such lazy fetching is particularly beneficial + * for read-only operations, in particular if the chances of resolving the + * result in the second-level cache are high. + *

As JTA and transactional JNDI DataSources already provide lazy enlistment + * of JDBC Connections, LazyConnectionDataSourceProxy does not add value with + * JTA (i.e. Spring's JtaTransactionManager) as transaction strategy. + * @see #setUseTransactionAwareDataSource + * @see HibernateTransactionManager + * @see org.springframework.transaction.jta.JtaTransactionManager + * @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy + */ + public This setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + return this.instance; + } + + /** + * Return the DataSource to be used by the SessionFactory. + */ + public DataSource getDataSource() { + return this.dataSource; + } + + + /** + * Set the location of a single Hibernate XML config file, for example as + * classpath resource "classpath:hibernate.cfg.xml". + *

Note: Can be omitted when all necessary properties and mapping + * resources are specified locally via this bean. + * @see org.hibernate.cfg.Configuration#configure(java.net.URL) + */ + public void setConfigLocation(Resource configLocation) { + this.configLocations = new Resource[] {configLocation}; + } + + /** + * Return the Hibernate properties, if any. Mainly available for + * configuration through property paths that specify individual keys. + */ + public Properties getHibernateProperties() { + if (this.hibernateProperties == null) { + this.hibernateProperties = new Properties(); + } + return this.hibernateProperties; + } + + /** + * Set locations of cacheable Hibernate mapping files, for example as web app + * resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location + * via Spring's resource abstraction, as long as the resource can be resolved + * in the file system. + *

Can be used to add to mappings from a Hibernate XML config file, + * or to specify all mappings locally. + * @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File) + */ + public void setCacheableMappingLocations(Resource[] cacheableMappingLocations) { + this.cacheableMappingLocations = cacheableMappingLocations; + } + + + /** + * Set the locations of multiple Hibernate XML config files, for example as + * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml". + *

Note: Can be omitted when all necessary properties and mapping + * resources are specified locally via this bean. + * @see org.hibernate.cfg.Configuration#configure(java.net.URL) + */ + public void setConfigLocations(Resource[] configLocations) { + this.configLocations = configLocations; + } + + /** + * Set the LobHandler to be used by the SessionFactory. + * Will be exposed at config time for UserType implementations. + * @see #getConfigTimeLobHandler + * @see org.hibernate.usertype.UserType + * @see org.springframework.orm.hibernate3.support.ClobStringType + * @see org.springframework.orm.hibernate3.support.BlobByteArrayType + * @see org.springframework.orm.hibernate3.support.BlobSerializableType + */ + public void setLobHandler(LobHandler lobHandler) { + this.lobHandler = lobHandler; + } + + /** + * Set the Hibernate RegionFactory to use for the SessionFactory. + * Allows for using a Spring-managed RegionFactory instance. + *

As of Hibernate 3.3, this is the preferred mechanism for configuring + * caches, superseding the {@link #setCacheProvider CacheProvider SPI}. + * For Hibernate 3.2 compatibility purposes, the accepted reference is of type + * Object: the actual type is org.hibernate.cache.RegionFactory. + *

Note: If this is set, the Hibernate settings should not define a + * cache provider to avoid meaningless double configuration. + * @see org.hibernate.cache.RegionFactory + */ + public This setCacheRegionFactory(Object cacheRegionFactory) { + this.cacheRegionFactory = cacheRegionFactory; + return instance; + } + + protected Object getCacheRegionFactory() { + return this.cacheRegionFactory; + } + + /** + * Set whether to expose a transaction-aware current Session from the + * SessionFactory's getCurrentSession() method, returning the + * Session that's associated with the current Spring-managed transaction, if any. + *

Default is "true", letting data access code work with the plain + * Hibernate SessionFactory and its getCurrentSession() method, + * while still being able to participate in current Spring-managed transactions: + * with any transaction management strategy, either local or JTA / EJB CMT, + * and any transaction synchronization mechanism, either Spring or JTA. + * Furthermore, getCurrentSession() will also seamlessly work with + * a request-scoped Session managed by OpenSessionInViewFilter/Interceptor. + *

Turn this flag off to expose the plain Hibernate SessionFactory with + * Hibernate's default getCurrentSession() behavior, supporting + * plain JTA synchronization only. Alternatively, simply override the + * corresponding Hibernate property "hibernate.current_session_context_class". + * @see SpringSessionContext + * @see org.hibernate.SessionFactory#getCurrentSession() + * @see org.springframework.transaction.jta.JtaTransactionManager + * @see HibernateTransactionManager + * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter + * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor + */ + public This setExposeTransactionAwareSessionFactory(boolean exposeTransactionAwareSessionFactory) { + this.exposeTransactionAwareSessionFactory = exposeTransactionAwareSessionFactory; + return this.instance; + } + + /** + * Return whether to expose a transaction-aware proxy for the SessionFactory. + */ + protected boolean isExposeTransactionAwareSessionFactory() { + return this.exposeTransactionAwareSessionFactory; + } + + /** + * Set whether to use a transaction-aware DataSource for the SessionFactory, + * i.e. whether to automatically wrap the passed-in DataSource with Spring's + * TransactionAwareDataSourceProxy. + *

Default is "false": SessionFactoryBuilder types are usually used with Spring's + * HibernateTransactionManager or JtaTransactionManager, both of which work nicely + * on a plain JDBC DataSource. Hibernate Sessions and their JDBC Connections are + * fully managed by the Hibernate/JTA transaction infrastructure in such a scenario. + *

If you switch this flag to "true", Spring's Hibernate access will be able to + * participate in JDBC-based transactions managed outside of Hibernate + * (for example, by Spring's DataSourceTransactionManager). This can be convenient + * if you need a different local transaction strategy for another O/R mapping tool, + * for example, but still want Hibernate access to join into those transactions. + *

A further benefit of this option is that plain Sessions opened directly + * via the SessionFactory, outside of Spring's Hibernate support, will still + * participate in active Spring-managed transactions. However, consider using + * Hibernate's getCurrentSession() method instead (see javadoc of + * "exposeTransactionAwareSessionFactory" property). + *

WARNING: When using a transaction-aware JDBC DataSource in combination + * with OpenSessionInViewFilter/Interceptor, whether participating in JTA or + * external JDBC-based transactions, it is strongly recommended to set Hibernate's + * Connection release mode to "after_transaction" or "after_statement", which + * guarantees proper Connection handling in such a scenario. In contrast to that, + * HibernateTransactionManager generally requires release mode "on_close". + *

Note: If you want to use Hibernate's Connection release mode "after_statement" + * with a DataSource specified on this SessionFactoryBuilder (for example, a + * JTA-aware DataSource fetched from JNDI), switch this setting to "true". + * Otherwise, the ConnectionProvider used underneath will vote against aggressive + * release and thus silently switch to release mode "after_transaction". + * @see #setDataSource + * @see #setExposeTransactionAwareSessionFactory + * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager + * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter + * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor + * @see HibernateTransactionManager + * @see org.springframework.transaction.jta.JtaTransactionManager + */ + public This setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) { + this.useTransactionAwareDataSource = useTransactionAwareDataSource; + return instance; + } + + /** + * Return whether to use a transaction-aware DataSource for the SessionFactory. + */ + protected boolean isUseTransactionAwareDataSource() { + return this.useTransactionAwareDataSource; + } + + /** + * Set the JTA TransactionManager to be used for Hibernate's + * TransactionManagerLookup. Allows for using a Spring-managed + * JTA TransactionManager for Hibernate's cache synchronization. + *

Note: If this is set, the Hibernate settings should not define a + * transaction manager lookup to avoid meaningless double configuration. + * @see LocalTransactionManagerLookup + */ + public This setJtaTransactionManager(TransactionManager jtaTransactionManager) { + this.jtaTransactionManager = jtaTransactionManager; + return instance; + } + + /** + * Set whether to execute a schema update after SessionFactory initialization. + *

For details on how to make schema update scripts work, see the Hibernate + * documentation, as this class leverages the same schema update script support + * in org.hibernate.cfg.Configuration as Hibernate's own SchemaUpdate tool. + * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript + * @see org.hibernate.tool.hbm2ddl.SchemaUpdate + */ + public This setSchemaUpdate(boolean schemaUpdate) { + this.schemaUpdate = schemaUpdate; + return this.instance; + } + + /** + * Set the beanClassLoader for this instance. Not named {@code setBeanClassLoader} + * to allow subclasses to implement {@code BeanClassLoaderAware} without violating + * this method's signature. Any such implementation should simply delegate to this + * method. + * @see #getBeanClassLoader() + */ + public This setClassLoader(ClassLoader beanClassLoader) { + this.beanClassLoader = beanClassLoader; + return this.instance; + } + + /** + * @see #setClassLoader(ClassLoader) + */ + protected ClassLoader getBeanClassLoader() { + return this.beanClassLoader; + } + + /** + * Set Hibernate mapping resources to be found in the class path, + * like "example.hbm.xml" or "mypackage/example.hbm.xml". + * Analogous to mapping entries in a Hibernate XML config file. + * Alternative to the more generic setMappingLocations method. + *

Can be used to add to mappings from a Hibernate XML config file, + * or to specify all mappings locally. + * @see #setMappingLocations + * @see org.hibernate.cfg.Configuration#addResource + */ + public This setMappingResources(String[] mappingResources) { + this.mappingResources = mappingResources; + return instance; + } + + /** + * Set locations of jar files that contain Hibernate mapping resources, + * like "WEB-INF/lib/example.hbm.jar". + *

Can be used to add to mappings from a Hibernate XML config file, + * or to specify all mappings locally. + * @see org.hibernate.cfg.Configuration#addJar(java.io.File) + */ + public This setMappingJarLocations(Resource[] mappingJarLocations) { + this.mappingJarLocations = mappingJarLocations; + return instance; + } + + /** + * Set locations of directories that contain Hibernate mapping resources, + * like "WEB-INF/mappings". + *

Can be used to add to mappings from a Hibernate XML config file, + * or to specify all mappings locally. + * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File) + */ + public This setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) { + this.mappingDirectoryLocations = mappingDirectoryLocations; + return instance; + } + + /** + * Set locations of Hibernate mapping files, for example as classpath + * resource "classpath:example.hbm.xml". Supports any resource location + * via Spring's resource abstraction, for example relative paths like + * "WEB-INF/mappings/example.hbm.xml" when running in an application context. + *

Can be used to add to mappings from a Hibernate XML config file, + * or to specify all mappings locally. + * @see org.hibernate.cfg.Configuration#addInputStream + */ + public This setMappingLocations(Resource[] mappingLocations) { + this.mappingLocations = mappingLocations; + return instance; + } + + /** + * Set Hibernate properties, such as "hibernate.dialect". + *

Can be used to override values in a Hibernate XML config file, + * or to specify all necessary properties locally. + *

Note: Do not specify a transaction provider here when using + * Spring-driven transactions. It is also advisable to omit connection + * provider settings and use a Spring-set DataSource instead. + * @see #setDataSource + */ + public This setHibernateProperties(Properties hibernateProperties) { + this.hibernateProperties = hibernateProperties; + return instance; + } + + /** + * Set a Hibernate entity interceptor that allows to inspect and change + * property values before writing to and reading from the database. + * Will get applied to any new Session created by this factory. + *

Such an interceptor can either be set at the SessionFactory level, i.e. on + * SessionFactoryBuilder, or at the Session level, i.e. on HibernateTemplate, + * HibernateInterceptor, and HibernateTransactionManager. It's preferable to set + * it on SessionFactoryBuilder or HibernateTransactionManager to avoid repeated + * configuration and guarantee consistent behavior in transactions. + * @see HibernateTemplate#setEntityInterceptor + * @see HibernateInterceptor#setEntityInterceptor + * @see HibernateTransactionManager#setEntityInterceptor + * @see org.hibernate.cfg.Configuration#setInterceptor + */ + public This setEntityInterceptor(Interceptor entityInterceptor) { + this.entityInterceptor = entityInterceptor; + return instance; + } + + /** + * Set a Hibernate NamingStrategy for the SessionFactory, determining the + * physical column and table names given the info in the mapping document. + * @see org.hibernate.cfg.Configuration#setNamingStrategy + */ + public This setNamingStrategy(NamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + return instance; + } + + /** + * Specify the cache strategies for entities (persistent classes or named entities). + * This configuration setting corresponds to the {@code } entry + * in the "hibernate.cfg.xml" configuration format. + *

For example: + *

+	 * {@code
+	 * 
+	 *   
+	 *     read-write
+	 *     read-only,myRegion
+	 *   
+	 * }
+ * @param entityCacheStrategies properties that define entity cache strategies, + * with class names as keys and cache concurrency strategies as values + * @see org.hibernate.cfg.Configuration#setCacheConcurrencyStrategy(String, String) + */ + public This setEntityCacheStrategies(Properties entityCacheStrategies) { + this.entityCacheStrategies = entityCacheStrategies; + return instance; + } + + /** + * Specify the cache strategies for persistent collections (with specific roles). + * This configuration setting corresponds to the {@code } entry + * in the "hibernate.cfg.xml" configuration format. + *

For example: + *

+	 * {@code
+	 * 
+	 *   
+	 *     read-write
+	 *     read-only,myRegion
+	 *   
+	 * }
+ * @param collectionCacheStrategies properties that define collection cache strategies, + * with collection roles as keys and cache concurrency strategies as values + * @see org.hibernate.cfg.Configuration#setCollectionCacheConcurrencyStrategy(String, String) + */ + public This setCollectionCacheStrategies(Properties collectionCacheStrategies) { + this.collectionCacheStrategies = collectionCacheStrategies; + return instance; + } + + /** + * Specify the Hibernate event listeners to register, with listener types + * as keys and listener objects as values. Instead of a single listener object, + * you can also pass in a list or set of listeners objects as value. + *

See the Hibernate documentation for further details on listener types + * and associated listener interfaces. + * @param eventListeners Map with listener type Strings as keys and + * listener objects as values + * @see org.hibernate.cfg.Configuration#setListener(String, Object) + */ + public This setEventListeners(Map eventListeners) { + this.eventListeners = eventListeners; + return this.instance; + } + + /** + * Specify the Hibernate FilterDefinitions to register with the SessionFactory. + * This is an alternative to specifying {@code } elements in + * Hibernate mapping files. + *

Typically, the passed-in FilterDefinition objects will have been defined + * as Spring FilterDefinitionFactoryBeans, probably as inner beans within the + * LocalSessionFactoryBean definition. + * @see FilterDefinitionFactoryBean + * @see org.hibernate.cfg.Configuration#addFilterDefinition + */ + public This setFilterDefinitions(FilterDefinition[] filterDefinitions) { + this.filterDefinitions = filterDefinitions; + return this.instance; + } + + /** + * Specify the Hibernate type definitions to register with the SessionFactory, + * as Spring TypeDefinitionBean instances. This is an alternative to specifying + * {@code } elements in Hibernate mapping files. + *

Unfortunately, Hibernate itself does not define a complete object that + * represents a type definition, hence the need for Spring's TypeDefinitionBean. + * @see TypeDefinitionBean + * @see org.hibernate.cfg.Mappings#addTypeDef(String, String, java.util.Properties) + */ + public This setTypeDefinitions(TypeDefinitionBean[] typeDefinitions) { + this.typeDefinitions = typeDefinitions; + return this.instance; + } + + /** + * Return the DataSource for the currently configured Hibernate SessionFactory, + * to be used by LocalDataSourceConnectionProvoder. + *

This instance will be set before initialization of the corresponding + * SessionFactory, and reset immediately afterwards. It is thus only available + * during configuration. + * @see #setDataSource + * @see LocalDataSourceConnectionProvider + */ + public static DataSource getConfigTimeDataSource() { + return configTimeDataSourceHolder.get(); + } + + /** + * Return the JTA TransactionManager for the currently configured Hibernate + * SessionFactory, to be used by LocalTransactionManagerLookup. + *

This instance will be set before initialization of the corresponding + * SessionFactory, and reset immediately afterwards. It is thus only available + * during configuration. + * @see #setJtaTransactionManager + * @see LocalTransactionManagerLookup + */ + public static TransactionManager getConfigTimeTransactionManager() { + return configTimeTransactionManagerHolder.get(); + } + + /** + * Return the RegionFactory for the currently configured Hibernate SessionFactory, + * to be used by LocalRegionFactoryProxy. + *

This instance will be set before initialization of the corresponding + * SessionFactory, and reset immediately afterwards. It is thus only available + * during configuration. + * @see #setCacheRegionFactory + */ + static Object getConfigTimeRegionFactory() { + return configTimeRegionFactoryHolder.get(); + } + + /** + * Return the LobHandler for the currently configured Hibernate SessionFactory, + * to be used by UserType implementations like ClobStringType. + *

This instance will be set before initialization of the corresponding + * SessionFactory, and reset immediately afterwards. It is thus only available + * during configuration. + * @see #setLobHandler + * @see org.springframework.orm.hibernate3.support.ClobStringType + * @see org.springframework.orm.hibernate3.support.BlobByteArrayType + * @see org.springframework.orm.hibernate3.support.BlobSerializableType + */ + public static LobHandler getConfigTimeLobHandler() { + return configTimeLobHandlerHolder.get(); + } + +} diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryUtils.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryUtils.java index ab6042cc206..2cacab471e1 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryUtils.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SessionFactoryUtils.java @@ -212,7 +212,7 @@ public abstract class SessionFactoryUtils { *

Supports setting a Session-level Hibernate entity interceptor that allows * to inspect and change property values before writing to and reading from the * database. Such an interceptor can also be set at the SessionFactory level - * (i.e. on LocalSessionFactoryBean), on HibernateTransactionManager, or on + * (i.e. on SessionFactoryBuilder), on HibernateTransactionManager, or on * HibernateInterceptor/HibernateTemplate. * @param sessionFactory Hibernate SessionFactory to create the session with * @param entityInterceptor Hibernate entity interceptor, or null if none @@ -221,7 +221,7 @@ public abstract class SessionFactoryUtils { * when actually registering a transaction synchronization) * @return the Hibernate Session * @throws DataAccessResourceFailureException if the Session couldn't be created - * @see LocalSessionFactoryBean#setEntityInterceptor + * @see SessionFactoryBuilder#setEntityInterceptor * @see HibernateInterceptor#setEntityInterceptor * @see HibernateTemplate#setEntityInterceptor */ diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SpringSessionContext.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SpringSessionContext.java index e914c19fe4b..77695e7ea43 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SpringSessionContext.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SpringSessionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import org.hibernate.engine.SessionFactoryImplementor; * that delegates to Spring's SessionFactoryUtils for providing a * Spring-managed current Session. * - *

Used by Spring's {@link LocalSessionFactoryBean} when told to expose a + *

Used by Spring's {@link SessionFactoryBuilder} when told to expose a * transaction-aware SessionFactory. This is the default as of Spring 2.5. * *

This CurrentSessionContext implementation can also be specified in custom @@ -36,8 +36,9 @@ import org.hibernate.engine.SessionFactoryImplementor; * @author Juergen Hoeller * @since 2.0 * @see SessionFactoryUtils#doGetSession - * @see LocalSessionFactoryBean#setExposeTransactionAwareSessionFactory + * @see SessionFactoryBuilder#setExposeTransactionAwareSessionFactory */ +@SuppressWarnings("serial") public class SpringSessionContext implements CurrentSessionContext { private final SessionFactoryImplementor sessionFactory; diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/TransactionAwareDataSourceConnectionProvider.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/TransactionAwareDataSourceConnectionProvider.java index e8233d2c1ea..a9425c1e78e 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/TransactionAwareDataSourceConnectionProvider.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/TransactionAwareDataSourceConnectionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,11 +23,11 @@ import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; /** * Subclass of LocalDataSourceConnectionProvider that returns a * transaction-aware proxy for the exposed DataSource. Used if - * LocalSessionFactoryBean's "useTransactionAwareDataSource" flag is on. + * SessionFactoryBuilder's "useTransactionAwareDataSource" flag is on. * * @author Juergen Hoeller * @since 1.2 - * @see LocalSessionFactoryBean#setUseTransactionAwareDataSource + * @see SessionFactoryBuilder#setUseTransactionAwareDataSource */ public class TransactionAwareDataSourceConnectionProvider extends LocalDataSourceConnectionProvider { diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/TypeDefinitionBean.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/TypeDefinitionBean.java index 68140afdf89..406c5a70f1a 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/TypeDefinitionBean.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/TypeDefinitionBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,19 +28,20 @@ import org.springframework.beans.factory.InitializingBean; * definition, as list element for the "typeDefinitions" bean property. * For example: * - *

- * <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
+ * 
+ * {@code
+ * 
  *   ...
- *   <property name="typeDefinitions">
- *     <list>
- *       <bean class="org.springframework.orm.hibernate3.TypeDefinitionBean">
- *         <property name="typeName" value="myType"/>
- *         <property name="typeClass" value="mypackage.MyTypeClass"/>
- *       </bean>
- *     </list>
- *   </property>
+ *   
+ *     
+ *       
+ *         
+ *         
+ *       
+ *     
+ *   
  *   ...
- * </bean>
+ * }
* * Alternatively, specify a bean id (or name) attribute for the inner bean, * instead of the "typeName" property. diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBean.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBean.java index a4649337ace..3ecc5c9423a 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBean.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,233 +16,169 @@ package org.springframework.orm.hibernate3.annotation; -import java.io.IOException; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.MappedSuperclass; - import org.hibernate.HibernateException; -import org.hibernate.MappingException; -import org.hibernate.cfg.AnnotationConfiguration; -import org.hibernate.cfg.Configuration; - +import org.hibernate.SessionFactory; import org.springframework.context.ResourceLoaderAware; -import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternUtils; -import org.springframework.core.type.classreading.CachingMetadataReaderFactory; -import org.springframework.core.type.classreading.MetadataReader; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.AnnotationTypeFilter; -import org.springframework.core.type.filter.TypeFilter; -import org.springframework.orm.hibernate3.LocalSessionFactoryBean; -import org.springframework.util.ClassUtils; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.support.SQLExceptionTranslator; +import org.springframework.orm.hibernate3.HibernateExceptionTranslator; +import org.springframework.orm.hibernate3.SessionFactoryBeanOperations; +import org.springframework.orm.hibernate3.SessionFactoryBeanDelegate; +import org.springframework.orm.hibernate3.SessionFactoryBuilderSupport; /** - * Subclass of Spring's standard LocalSessionFactoryBean for Hibernate, - * supporting JDK 1.5+ annotation metadata for mappings. + * Subclass of {@link AnnotationSessionFactoryBuilder} adhering to Spring's + * {@link org.springframework.beans.factory.FactoryBean FactoryBean} contract, + * making it suitable for use in XML configuration. * - *

Note: This class requires Hibernate 3.2 or later, with the - * Java Persistence API and the Hibernate Annotations add-on present. - * - *

Example for an AnnotationSessionFactoryBean bean definition: + *

A typical {@code AnnotationSessionFactoryBean} bean definition: * *

- * <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
- *   <property name="dataSource" ref="dataSource"/>
- *   <property name="annotatedClasses">
- *     <list>
- *       <value>test.package.Foo</value>
- *       <value>test.package.Bar</value>
- *     </list>
- *   </property>
- * </bean>
+ * {@code + * + * + * + * + * test.package.Foo + * test.package.Bar + * + * + * } * * Or when using classpath scanning for autodetection of entity classes: * *
- * <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
- *   <property name="dataSource" ref="dataSource"/>
- *   <property name="packagesToScan" value="test.package"/>
- * </bean>
+ * {@code + * + * + * + * } + * + *

Implements the + * {@link org.springframework.dao.support.PersistenceExceptionTranslator + * PersistenceExceptionTranslator} interface, as autodetected by Spring's {@link + * org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor + * PersistenceExceptionTranslationPostProcessor}, for AOP-based translation of + * native Hibernate exceptions to Spring's {@link DataAccessException} hierarchy. + * Hence, the presence of an {@code AnnotationSessionFactoryBean} automatically + * enables a {@code PersistenceExceptionTranslationPostProcessor} to translate + * Hibernate exceptions. * * @author Juergen Hoeller + * @author Chris Beams * @since 1.2.2 - * @see #setDataSource - * @see #setHibernateProperties - * @see #setAnnotatedClasses - * @see #setAnnotatedPackages + * @see SessionFactoryBuilderSupport + * @see AnnotationSessionFactoryBuilder */ -public class AnnotationSessionFactoryBean extends LocalSessionFactoryBean implements ResourceLoaderAware { +public class AnnotationSessionFactoryBean extends AnnotationSessionFactoryBuilder + implements SessionFactoryBeanOperations, ResourceLoaderAware { - private static final String RESOURCE_PATTERN = "/**/*.class"; + private final SessionFactoryBeanDelegate delegate = new SessionFactoryBeanDelegate(this); + @Deprecated + public void setCacheProvider(org.hibernate.cache.CacheProvider cacheProvider) { + delegate.setCacheProvider(cacheProvider); + } - private Class[] annotatedClasses; - - private String[] annotatedPackages; - - private String[] packagesToScan; - - private TypeFilter[] entityTypeFilters = new TypeFilter[] { - new AnnotationTypeFilter(Entity.class, false), - new AnnotationTypeFilter(Embeddable.class, false), - new AnnotationTypeFilter(MappedSuperclass.class, false), - new AnnotationTypeFilter(org.hibernate.annotations.Entity.class, false)}; + @Override + protected void preBuildSessionFactory() { + delegate.preBuildSessionFactory(); + } - private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + @Override + protected void postBuildSessionFactory() { + delegate.postBuildSessionFactory(); + } + public void setResourceLoader(ResourceLoader resourceLoader) { + this.setResourcePatternResolver(ResourcePatternUtils.getResourcePatternResolver(resourceLoader)); + } - public AnnotationSessionFactoryBean() { - setConfigurationClass(AnnotationConfiguration.class); + public void destroy() throws HibernateException { + delegate.destroy(); } + public SessionFactory getObject() { + return delegate.getObject(); + } - @Override - public void setConfigurationClass(Class configurationClass) { - if (configurationClass == null || !AnnotationConfiguration.class.isAssignableFrom(configurationClass)) { - throw new IllegalArgumentException( - "AnnotationSessionFactoryBean only supports AnnotationConfiguration or subclasses"); - } - super.setConfigurationClass(configurationClass); + public Class getObjectType() { + return delegate.getObjectType(); } - /** - * Specify annotated classes, for which mappings will be read from - * class-level JDK 1.5+ annotation metadata. - * @see org.hibernate.cfg.AnnotationConfiguration#addAnnotatedClass(Class) - */ - public void setAnnotatedClasses(Class[] annotatedClasses) { - this.annotatedClasses = annotatedClasses; + public void setBeanClassLoader(ClassLoader beanClassLoader) { + delegate.setBeanClassLoader(beanClassLoader); } - /** - * Specify the names of annotated packages, for which package-level - * JDK 1.5+ annotation metadata will be read. - * @see org.hibernate.cfg.AnnotationConfiguration#addPackage(String) - */ - public void setAnnotatedPackages(String[] annotatedPackages) { - this.annotatedPackages = annotatedPackages; + public boolean isSingleton() { + return delegate.isSingleton(); } - /** - * Set whether to use Spring-based scanning for entity classes in the classpath - * instead of listing annotated classes explicitly. - *

Default is none. Specify packages to search for autodetection of your entity - * classes in the classpath. This is analogous to Spring's component-scan feature - * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}). - */ - public void setPackagesToScan(String[] packagesToScan) { - this.packagesToScan = packagesToScan; + public void afterPropertiesSet() throws Exception { + delegate.afterPropertiesSet(); } - /** - * Specify custom type filters for Spring-based scanning for entity classes. - *

Default is to search all specified packages for classes annotated with - * @javax.persistence.Entity, @javax.persistence.Embeddable - * or @javax.persistence.MappedSuperclass, as well as for - * Hibernate's special @org.hibernate.annotations.Entity. - * @see #setPackagesToScan - */ - public void setEntityTypeFilters(TypeFilter[] entityTypeFilters) { - this.entityTypeFilters = entityTypeFilters; + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + return delegate.translateExceptionIfPossible(ex); } - public void setResourceLoader(ResourceLoader resourceLoader) { - this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); + public void setJdbcExceptionTranslator( + SQLExceptionTranslator jdbcExceptionTranslator) { + delegate.setJdbcExceptionTranslator(jdbcExceptionTranslator); } + public void setPersistenceExceptionTranslator( + HibernateExceptionTranslator hibernateExceptionTranslator) { + delegate.setPersistenceExceptionTranslator(hibernateExceptionTranslator); + } /** - * Reads metadata from annotated classes and packages into the - * AnnotationConfiguration instance. + * @deprecated as of Spring 3.1 in favor of {@link #scanPackages()} which + * can access the internal {@code AnnotationConfiguration} instance via + * {@link #getConfiguration()}. */ - @Override - protected void postProcessMappings(Configuration config) throws HibernateException { - AnnotationConfiguration annConfig = (AnnotationConfiguration) config; - if (this.annotatedClasses != null) { - for (Class annotatedClass : this.annotatedClasses) { - annConfig.addAnnotatedClass(annotatedClass); - } - } - if (this.annotatedPackages != null) { - for (String annotatedPackage : this.annotatedPackages) { - annConfig.addPackage(annotatedPackage); - } - } - scanPackages(annConfig); + @Deprecated + protected void scanPackages(org.hibernate.cfg.AnnotationConfiguration config) { + this.scanPackages(); } /** - * Perform Spring-based scanning for entity classes. - * @see #setPackagesToScan + * @deprecated as of Spring 3.1 in favor of {@link #newSessionFactory()} which + * can access the internal {@code Configuration} instance via {@link #getConfiguration()}. */ - protected void scanPackages(AnnotationConfiguration config) { - if (this.packagesToScan != null) { - try { - for (String pkg : this.packagesToScan) { - String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + - ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN; - Resource[] resources = this.resourcePatternResolver.getResources(pattern); - MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver); - for (Resource resource : resources) { - if (resource.isReadable()) { - MetadataReader reader = readerFactory.getMetadataReader(resource); - String className = reader.getClassMetadata().getClassName(); - if (matchesFilter(reader, readerFactory)) { - config.addAnnotatedClass(this.resourcePatternResolver.getClassLoader().loadClass(className)); - } - } - } - } - } - catch (IOException ex) { - throw new MappingException("Failed to scan classpath for unlisted classes", ex); - } - catch (ClassNotFoundException ex) { - throw new MappingException("Failed to load annotated classes from classpath", ex); - } - } + @Deprecated + protected SessionFactory newSessionFactory(org.hibernate.cfg.Configuration config) throws HibernateException { + return this.newSessionFactory(); } /** - * Check whether any of the configured entity type filters matches - * the current class descriptor contained in the metadata reader. + * @deprecated as of Spring 3.1 in favor of {@link #postProcessMappings()} which + * can access the internal {@code Configuration} instance via {@link #getConfiguration()}. */ - private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException { - if (this.entityTypeFilters != null) { - for (TypeFilter filter : this.entityTypeFilters) { - if (filter.match(reader, readerFactory)) { - return true; - } - } - } - return false; + @Deprecated + protected void postProcessMappings(org.hibernate.cfg.Configuration config) throws HibernateException { + this.postProcessMappings(); } - /** - * This default implementation delegates to {@link #postProcessAnnotationConfiguration}. + * @deprecated as of Spring 3.1 in favor of {@link #postProcessConfiguration()} which + * can access the internal {@code Configuration} instance via {@link #getConfiguration()}. */ - @Override - protected void postProcessConfiguration(Configuration config) throws HibernateException { - postProcessAnnotationConfiguration((AnnotationConfiguration) config); + @Deprecated + protected void postProcessConfiguration(org.hibernate.cfg.Configuration config) throws HibernateException { + this.postProcessConfiguration(); } /** - * To be implemented by subclasses which want to to perform custom - * post-processing of the AnnotationConfiguration object after this - * FactoryBean performed its default initialization. - *

Note: As of Hibernate 3.6, AnnotationConfiguration's features - * have been rolled into Configuration itself. Simply overriding - * {@link #postProcessConfiguration(org.hibernate.cfg.Configuration)} - * becomes an option as well then. - * @param config the current AnnotationConfiguration object - * @throws HibernateException in case of Hibernate initialization errors + * @deprecated as of Spring 3.1 in favor of {@link #postProcessAnnotationConfiguration()} + * which can access the internal {@code AnnotationConfiguration} instance via + * {@link #getConfiguration()}. */ - protected void postProcessAnnotationConfiguration(AnnotationConfiguration config) throws HibernateException { + @Deprecated + protected void postProcessAnnotationConfiguration(org.hibernate.cfg.AnnotationConfiguration config) throws HibernateException { + this.postProcessAnnotationConfiguration(); } } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBuilder.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBuilder.java new file mode 100644 index 00000000000..af291fb2d81 --- /dev/null +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBuilder.java @@ -0,0 +1,300 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate3.annotation; + +import java.io.IOException; +import java.lang.reflect.Method; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.MappedSuperclass; +import javax.sql.DataSource; + +import org.hibernate.HibernateException; +import org.hibernate.MappingException; +import org.hibernate.cfg.AnnotationConfiguration; +import org.hibernate.cfg.Configuration; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.orm.hibernate3.SessionFactoryBuilderSupport; +import org.springframework.util.ClassUtils; + +/** + * Hibernate {@link AnnotationConfiguration} builder suitable for use within Spring + * {@link org.springframework.context.annotation.Configuration @Configuration} + * class {@link Bean @Bean} methods. For complete details on features, see the + * JavaDoc for the {@link SessionFactoryBuilderSupport} superclass. For use in + * Spring XML configuration, see the {@link AnnotationSessionFactoryBean} subclass. + * + *

As noted in {@code SessionFactoryBuilderSupport} JavaDoc, this class requires + * Hibernate 3.2 or later; it additionally requires that the Java Persistence API + * and Hibernate Annotations add-ons are present. + * + *

Setter methods return the builder instance in order to facilitate + * a concise and convenient method-chaining style. For example: + *

+ * {@code @Configuration}
+ * public class DataConfig {
+ *     {@code @Bean}
+ *     public SessionFactory sessionFactory() {
+ *         return new AnnotationSessionFactoryBuilder()
+ *             .setDataSource(dataSource())
+ *             .setPackagesToScan("com.myco"})
+ *             .buildSessionFactory();
+ *     }
+ * }
+ * 
+ * + *

Most Hibernate configuration operations can be performed directly against + * this API; however you may also access access and configure the underlying + * {@code AnnotationConfiguration} object by using the {@link #doWithConfiguration} + * method and providing a {@code HibernateConfigurationCallback} as follows: + *

+ * SessionFactory sessionFactory =
+ *     new AnnotationSessionFactoryBuilder()
+ *         // ...
+ *         .doWithConfiguration(new HibernateConfigurationCallback<AnnotationConfiguration>() {
+ *             public void configure(AnnotationConfiguration cfg) {
+ *                 cfg.addAnnotatedClass(Foo.class);
+ *             }
+ *          })
+ *         .buildSessionFactory();
+ * 
+ * + * @author Juergen Hoeller + * @author Chris Beams + * @since 3.1 + * @see org.springframework.orm.hibernate3.SessionFactoryBuilderSupport + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder + * @see AnnotationSessionFactoryBean + */ +public class AnnotationSessionFactoryBuilder extends SessionFactoryBuilderSupport { + + /** Hibernate 3.6 consolidates Configuration and AnnotationConfiguration operations. */ + private static final boolean hibernate36Present = ClassUtils.hasMethod(Configuration.class, "addAnnotatedClass", Class.class); + + private static final String RESOURCE_PATTERN = "/**/*.class"; + + private Class[] annotatedClasses; + + private String[] annotatedPackages; + + private String[] packagesToScan; + + private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + + private TypeFilter[] entityTypeFilters = new TypeFilter[] { + new AnnotationTypeFilter(Entity.class, false), + new AnnotationTypeFilter(Embeddable.class, false), + new AnnotationTypeFilter(MappedSuperclass.class, false), + new AnnotationTypeFilter(org.hibernate.annotations.Entity.class, false)}; + + + /** + * Construct a new {@code AnnotationSessionFactoryBuilder} + */ + public AnnotationSessionFactoryBuilder() { + super(); + } + + /** + * Construct a new {@code AnnotationSessionFactoryBuilder} with the given + * Spring-managed {@code DataSource} instance. + * @see #setDataSource + */ + public AnnotationSessionFactoryBuilder(DataSource dataSource) { + super(dataSource); + } + + /** + * {@inheritDoc} + *

This implementation returns {@link org.hibernate.cfg.Configuration} if + * Hibernate 3.6 or greater is available on the runtime classpath, otherwise + * {@link org.hibernate.cfg.AnnotationConfiguration}. This accommodates + * the consolidation of these two types and deprecation of the latter in + * Hibernate 3.6. + * @see #doWithConfiguration + */ + protected Class getDefaultConfigurationClass() { + return hibernate36Present ? Configuration.class : AnnotationConfiguration.class; + } + + /** + * Set whether to use Spring-based scanning for entity classes in the classpath + * instead of listing annotated classes explicitly. + *

Default is none. Specify packages to search for autodetection of your entity + * classes in the classpath. This is analogous to Spring's component-scan feature + * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}). + */ + public AnnotationSessionFactoryBuilder setPackagesToScan(String... packagesToScan) { + this.packagesToScan = packagesToScan; + return this; + } + + /** + * Specify annotated classes, for which mappings will be read from + * class-level JDK 1.5+ annotation metadata. + * @see org.hibernate.cfg.AnnotationConfiguration#addAnnotatedClass + */ + public AnnotationSessionFactoryBuilder setAnnotatedClasses(Class... annotatedClasses) { + this.annotatedClasses = annotatedClasses; + return this; + } + + /** + * Specify the names of annotated packages, for which package-level + * JDK 1.5+ annotation metadata will be read. + * @see org.hibernate.cfg.AnnotationConfiguration#addPackage + */ + public AnnotationSessionFactoryBuilder setAnnotatedPackages(String[] annotatedPackages) { + this.annotatedPackages = annotatedPackages; + return this; + } + + /** + * Specify custom type filters for Spring-based scanning for entity classes. + *

Default is to search all specified packages for classes annotated with + * @javax.persistence.Entity, @javax.persistence.Embeddable + * or @javax.persistence.MappedSuperclass, as well as for + * Hibernate's special @org.hibernate.annotations.Entity. + * @see #setPackagesToScan + */ + public AnnotationSessionFactoryBuilder setEntityTypeFilters(TypeFilter[] entityTypeFilters) { + this.entityTypeFilters = entityTypeFilters; + return this; + } + + public void setResourcePatternResolver(ResourcePatternResolver resourcePatternResolver) { + this.resourcePatternResolver = resourcePatternResolver; + } + + /** + * Perform Spring-based scanning for entity classes. + * @see #setPackagesToScan + */ + protected void scanPackages() { + if (this.packagesToScan != null) { + try { + for (String pkg : this.packagesToScan) { + String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN; + Resource[] resources = this.resourcePatternResolver.getResources(pattern); + MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver); + for (Resource resource : resources) { + if (resource.isReadable()) { + MetadataReader reader = readerFactory.getMetadataReader(resource); + String className = reader.getClassMetadata().getClassName(); + if (matchesFilter(reader, readerFactory)) { + Class annotatedClass = this.resourcePatternResolver.getClassLoader().loadClass(className); + invokeConfigurationMethod("addAnnotatedClass", Class.class, annotatedClass); + } + } + } + } + } + catch (IOException ex) { + throw new MappingException("Failed to scan classpath for unlisted classes", ex); + } + catch (ClassNotFoundException ex) { + throw new MappingException("Failed to load annotated classes from classpath", ex); + } + } + } + + /** + * Check whether any of the configured entity type filters matches + * the current class descriptor contained in the metadata reader. + */ + private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException { + if (this.entityTypeFilters != null) { + for (TypeFilter filter : this.entityTypeFilters) { + if (filter.match(reader, readerFactory)) { + return true; + } + } + } + return false; + } + + /** + * Reads metadata from annotated classes and packages into the + * AnnotationConfiguration instance. + */ + @Override + protected void postProcessMappings() throws HibernateException { + // org.hibernate.cfg.Configuration cfg = getConfiguration(); + if (this.annotatedClasses != null) { + for (Class annotatedClass : this.annotatedClasses) { + invokeConfigurationMethod("addAnnotatedClass", Class.class, annotatedClass); + } + } + if (this.annotatedPackages != null) { + for (String annotatedPackage : this.annotatedPackages) { + invokeConfigurationMethod("addPackage", String.class, annotatedPackage); + } + } + scanPackages(); + } + + /** + * Delegates to {@link #postProcessAnnotationConfiguration}. + */ + @Override + protected void postProcessConfiguration() throws HibernateException { + postProcessAnnotationConfiguration(); + } + + /** + * To be implemented by subclasses which want to to perform custom + * post-processing of the AnnotationConfiguration object after this + * FactoryBean performed its default initialization. + *

Note: As of Hibernate 3.6, AnnotationConfiguration's features + * have been rolled into Configuration itself. Simply overriding + * {@link #postProcessConfiguration(org.hibernate.cfg.Configuration)} + * becomes an option as well then. + * @param config the current AnnotationConfiguration object + * @throws HibernateException in case of Hibernate initialization errors + */ + protected void postProcessAnnotationConfiguration() throws HibernateException { + } + + /** + * Reflectively invoke the given method against the underlying Configuration + * instance. In order to support the consolidation of Hibernate's + * AnnotationConfiguration into Configuration in Hibernate 3.6 while continuing + * to to compile against Hibernate 3.3.x, we must reflectively invoke in order + * to avoid compilation failure. + */ + private void invokeConfigurationMethod(String methodName, Class parameterType, T parameter) { + org.hibernate.cfg.Configuration cfg = getConfiguration(); + try { + Method m = cfg.getClass().getMethod(methodName, parameterType); + m.invoke(cfg, parameter); + } catch (Exception ex) { + throw new IllegalStateException(String.format("Hibernate Configuration class [%s] does not support the '%s(%s)' method.", + cfg.getClass().getSimpleName(), methodName, parameterType.getSimpleName())); + } + } + +} diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/AbstractLobType.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/AbstractLobType.java index 5ab3c68bbbe..3c8fa4ac545 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/AbstractLobType.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/AbstractLobType.java @@ -22,7 +22,6 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import javax.transaction.Status; import javax.transaction.TransactionManager; import org.apache.commons.logging.Log; @@ -30,23 +29,18 @@ import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.usertype.UserType; import org.hibernate.util.EqualsHelper; - -import org.springframework.dao.DataAccessResourceFailureException; -import org.springframework.jdbc.support.lob.JtaLobCreatorSynchronization; import org.springframework.jdbc.support.lob.LobCreator; -import org.springframework.jdbc.support.lob.LobHandler; -import org.springframework.jdbc.support.lob.SpringLobCreatorSynchronization; import org.springframework.jdbc.support.lob.LobCreatorUtils; -import org.springframework.orm.hibernate3.LocalSessionFactoryBean; -import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.jdbc.support.lob.LobHandler; +import org.springframework.orm.hibernate3.SessionFactoryBuilderSupport; /** * Abstract base class for Hibernate UserType implementations that map to LOBs. - * Retrieves the LobHandler to use from LocalSessionFactoryBean at config time. + * Retrieves the LobHandler to use from SessionFactoryBuilder at config time. * *

For writing LOBs, either an active Spring transaction synchronization * or an active JTA transaction (with "jtaTransactionManager" specified on - * LocalSessionFactoryBean or a Hibernate TransactionManagerLookup configured + * SessionFactoryBuilder or a Hibernate TransactionManagerLookup configured * through the corresponding Hibernate property) is required. * *

Offers template methods for setting parameters and getting result values, @@ -56,8 +50,8 @@ import org.springframework.transaction.support.TransactionSynchronizationManager * @since 1.2 * @see org.springframework.jdbc.support.lob.LobHandler * @see org.springframework.jdbc.support.lob.LobCreator - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setJtaTransactionManager + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#setLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#setJtaTransactionManager */ public abstract class AbstractLobType implements UserType { @@ -70,13 +64,13 @@ public abstract class AbstractLobType implements UserType { /** * Constructor used by Hibernate: fetches config-time LobHandler and - * config-time JTA TransactionManager from LocalSessionFactoryBean. - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager + * config-time JTA TransactionManager from the SessionFactory builder. + * @see org.springframework.orm.hibernate3.SessionFactoryBuilderSupport#getConfigTimeLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilderSupport#getConfigTimeTransactionManager */ protected AbstractLobType() { - this(LocalSessionFactoryBean.getConfigTimeLobHandler(), - LocalSessionFactoryBean.getConfigTimeTransactionManager()); + this(SessionFactoryBuilderSupport.getConfigTimeLobHandler(), + SessionFactoryBuilderSupport.getConfigTimeTransactionManager()); } /** @@ -150,7 +144,7 @@ public abstract class AbstractLobType implements UserType { if (this.lobHandler == null) { throw new IllegalStateException("No LobHandler found for configuration - " + - "lobHandler property must be set on LocalSessionFactoryBean"); + "lobHandler property must be set on SessionFactoryBuilder"); } try { @@ -172,7 +166,7 @@ public abstract class AbstractLobType implements UserType { if (this.lobHandler == null) { throw new IllegalStateException("No LobHandler found for configuration - " + - "lobHandler property must be set on LocalSessionFactoryBean"); + "lobHandler property must be set on SessionFactoryBuilder"); } LobCreator lobCreator = this.lobHandler.getLobCreator(); diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobByteArrayType.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobByteArrayType.java index c30f81fdb1b..d26b2c26215 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobByteArrayType.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobByteArrayType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ import org.springframework.jdbc.support.lob.LobHandler; /** * Hibernate UserType implementation for byte arrays that get mapped to BLOBs. - * Retrieves the LobHandler to use from LocalSessionFactoryBean at config time. + * Retrieves the LobHandler to use from SessionFactoryBuilder at config time. * *

Can also be defined in generic Hibernate mappings, as DefaultLobCreator will * work with most JDBC-compliant database drivers. In this case, the field type @@ -38,15 +38,15 @@ import org.springframework.jdbc.support.lob.LobHandler; * * @author Juergen Hoeller * @since 1.2 - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#setLobHandler */ public class BlobByteArrayType extends AbstractLobType { /** * Constructor used by Hibernate: fetches config-time LobHandler and - * config-time JTA TransactionManager from LocalSessionFactoryBean. - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager + * config-time JTA TransactionManager from SessionFactoryBuilder. + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#getConfigTimeLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#getConfigTimeTransactionManager */ public BlobByteArrayType() { super(); @@ -64,7 +64,7 @@ public class BlobByteArrayType extends AbstractLobType { return new int[] {Types.BLOB}; } - public Class returnedClass() { + public Class returnedClass() { return byte[].class; } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobSerializableType.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobSerializableType.java index 9334c8db88c..ba57c0f778f 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobSerializableType.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobSerializableType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ import org.springframework.jdbc.support.lob.LobHandler; /** * Hibernate UserType implementation for arbitrary objects that get serialized to BLOBs. - * Retrieves the LobHandler to use from LocalSessionFactoryBean at config time. + * Retrieves the LobHandler to use from SessionFactoryBuilder at config time. * *

Can also be defined in generic Hibernate mappings, as DefaultLobCreator will * work with most JDBC-compliant database drivers. In this case, the field type @@ -46,7 +46,7 @@ import org.springframework.jdbc.support.lob.LobHandler; * * @author Juergen Hoeller * @since 1.2 - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#setLobHandler */ public class BlobSerializableType extends AbstractLobType { @@ -60,9 +60,9 @@ public class BlobSerializableType extends AbstractLobType { /** * Constructor used by Hibernate: fetches config-time LobHandler and - * config-time JTA TransactionManager from LocalSessionFactoryBean. - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager + * config-time JTA TransactionManager from SessionFactoryBuilder. + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#getConfigTimeLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#getConfigTimeTransactionManager */ public BlobSerializableType() { super(); @@ -80,7 +80,7 @@ public class BlobSerializableType extends AbstractLobType { return new int[] {Types.BLOB}; } - public Class returnedClass() { + public Class returnedClass() { return Serializable.class; } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobStringType.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobStringType.java index be7ac8f15c6..9118906cf9a 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobStringType.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/BlobStringType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ import org.springframework.jdbc.support.lob.LobHandler; /** * Hibernate UserType implementation for Strings that get mapped to BLOBs. - * Retrieves the LobHandler to use from LocalSessionFactoryBean at config time. + * Retrieves the LobHandler to use from SessionFactoryBuilder at config time. * *

This is intended for the (arguably unnatural, but still common) case * where character data is stored in a binary LOB. This requires encoding @@ -44,15 +44,15 @@ import org.springframework.jdbc.support.lob.LobHandler; * @author Juergen Hoeller * @since 1.2.7 * @see #getCharacterEncoding() - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#setLobHandler */ public class BlobStringType extends AbstractLobType { /** * Constructor used by Hibernate: fetches config-time LobHandler and - * config-time JTA TransactionManager from LocalSessionFactoryBean. - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager + * config-time JTA TransactionManager from SessionFactoryBuilder. + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#getConfigTimeLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#getConfigTimeTransactionManager */ public BlobStringType() { super(); @@ -70,7 +70,7 @@ public class BlobStringType extends AbstractLobType { return new int[] {Types.BLOB}; } - public Class returnedClass() { + public Class returnedClass() { return String.class; } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/ClobStringType.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/ClobStringType.java index 12435ba890c..18ad3478609 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/ClobStringType.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/ClobStringType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import org.springframework.jdbc.support.lob.LobHandler; /** * Hibernate UserType implementation for Strings that get mapped to CLOBs. - * Retrieves the LobHandler to use from LocalSessionFactoryBean at config time. + * Retrieves the LobHandler to use from SessionFactoryBuilder at config time. * *

Particularly useful for storing Strings with more than 4000 characters in an * Oracle database (only possible via CLOBs), in combination with OracleLobHandler. @@ -40,15 +40,15 @@ import org.springframework.jdbc.support.lob.LobHandler; * * @author Juergen Hoeller * @since 1.2 - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#setLobHandler */ public class ClobStringType extends AbstractLobType { /** * Constructor used by Hibernate: fetches config-time LobHandler and - * config-time JTA TransactionManager from LocalSessionFactoryBean. - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager + * config-time JTA TransactionManager from SessionFactoryBuilder. + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#getConfigTimeLobHandler + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#getConfigTimeTransactionManager */ public ClobStringType() { super(); @@ -66,7 +66,7 @@ public class ClobStringType extends AbstractLobType { return new int[] {Types.CLOB}; } - public Class returnedClass() { + public Class returnedClass() { return String.class; } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/IdTransferringMergeEventListener.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/IdTransferringMergeEventListener.java index 4392b39e9fd..9052419c7e4 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/IdTransferringMergeEventListener.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/IdTransferringMergeEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,19 +42,21 @@ import org.hibernate.persister.entity.EntityPersister; * calls, with the Hibernate SessionFactory configuration specifying an * IdTransferringMergeEventListener. * - *

Typically specified as entry for LocalSessionFactoryBean's "eventListeners" + *

Typically specified as entry for SessionFactoryBuilder's "eventListeners" * map, with key "merge". * * @author Juergen Hoeller * @since 1.2 - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setEventListeners(java.util.Map) + * @see org.springframework.orm.hibernate3.SessionFactoryBuilder#setEventListeners(java.util.Map) */ +@SuppressWarnings("serial") public class IdTransferringMergeEventListener extends DefaultMergeEventListener { /** * Hibernate 3.1 implementation of ID transferral. */ @Override + @SuppressWarnings("rawtypes") protected void entityIsTransient(MergeEvent event, Map copyCache) { super.entityIsTransient(event, copyCache); SessionImplementor session = event.getSession(); diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/ScopedBeanInterceptor.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/ScopedBeanInterceptor.java index e2bc17731f0..4f24d13573a 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/ScopedBeanInterceptor.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/ScopedBeanInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,18 +30,20 @@ import org.springframework.aop.support.AopUtils; * *

Usage example: * - *

- * <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
+ * 
+ * {@code
+ * 
  *   ...
- *   <property name="entityInterceptor">
- *     <bean class="org.springframework.orm.hibernate3.support.ScopedBeanInterceptor"/>
- *   </property>
- * </bean>
+ * + * + * + * }
* * @author Costin Leau * @author Juergen Hoeller * @since 2.0 */ +@SuppressWarnings("serial") public class ScopedBeanInterceptor extends EmptyInterceptor { @Override diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateTransactionManagerTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateTransactionManagerTests.java index 81a4bf6ea46..83b5b6771c0 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateTransactionManagerTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateTransactionManagerTests.java @@ -117,7 +117,8 @@ public class HibernateTransactionManagerTests extends TestCase { queryControl.replay(); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { - protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { + @Override + protected SessionFactory newSessionFactory() throws HibernateException { return sf; } }; @@ -339,7 +340,8 @@ public class HibernateTransactionManagerTests extends TestCase { queryControl.replay(); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { - protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { + @Override + protected SessionFactory newSessionFactory() throws HibernateException { return sf; } }; @@ -416,7 +418,8 @@ public class HibernateTransactionManagerTests extends TestCase { txControl.replay(); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { - protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { + @Override + protected SessionFactory newSessionFactory() throws HibernateException { return sf; } }; @@ -770,7 +773,8 @@ public class HibernateTransactionManagerTests extends TestCase { sessionControl.replay(); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { - protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { + @Override + protected SessionFactory newSessionFactory() throws HibernateException { return sf; } }; @@ -854,7 +858,8 @@ public class HibernateTransactionManagerTests extends TestCase { txControl.replay(); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { - protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { + @Override + protected SessionFactory newSessionFactory() throws HibernateException { return sf; } }; diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/LocalSessionFactoryBeanTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/LocalSessionFactoryBeanTests.java index c8e4254046c..115502ad0f5 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/LocalSessionFactoryBeanTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/LocalSessionFactoryBeanTests.java @@ -28,9 +28,11 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; + import javax.transaction.TransactionManager; import junit.framework.TestCase; + import org.easymock.MockControl; import org.hibernate.Hibernate; import org.hibernate.HibernateException; @@ -50,7 +52,6 @@ import org.hibernate.engine.FilterDefinition; import org.hibernate.event.MergeEvent; import org.hibernate.event.MergeEventListener; import org.hibernate.mapping.TypeDef; - import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.FileSystemResource; @@ -59,14 +60,17 @@ import org.springframework.jdbc.datasource.DriverManagerDataSource; /** * @author Juergen Hoeller + * @author Chris Beams * @since 05.03.2005 */ +@SuppressWarnings("serial") public class LocalSessionFactoryBeanTests extends TestCase { public void testLocalSessionFactoryBeanWithDataSource() throws Exception { final DriverManagerDataSource ds = new DriverManagerDataSource(); final List invocations = new ArrayList(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration addInputStream(InputStream is) { @@ -80,10 +84,11 @@ public class LocalSessionFactoryBeanTests extends TestCase { } }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { assertEquals(LocalDataSourceConnectionProvider.class.getName(), - config.getProperty(Environment.CONNECTION_PROVIDER)); - assertEquals(ds, LocalSessionFactoryBean.getConfigTimeDataSource()); + this.getConfiguration().getProperty(Environment.CONNECTION_PROVIDER)); + assertEquals(ds, SessionFactoryBuilderSupport.getConfigTimeDataSource()); invocations.add("newSessionFactory"); return null; } @@ -98,6 +103,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { final RegionFactory regionFactory = new NoCachingRegionFactory(null); final List invocations = new ArrayList(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration addInputStream(InputStream is) { @@ -111,10 +117,11 @@ public class LocalSessionFactoryBeanTests extends TestCase { } }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { assertEquals(LocalRegionFactoryProxy.class.getName(), - config.getProperty(Environment.CACHE_REGION_FACTORY)); - assertSame(regionFactory, LocalSessionFactoryBean.getConfigTimeRegionFactory()); + this.getConfiguration().getProperty(Environment.CACHE_REGION_FACTORY)); + assertSame(regionFactory, SessionFactoryBuilderSupport.getConfigTimeRegionFactory()); invocations.add("newSessionFactory"); return null; } @@ -125,10 +132,13 @@ public class LocalSessionFactoryBeanTests extends TestCase { assertEquals("newSessionFactory", invocations.get(0)); } + @SuppressWarnings("deprecation") + // CacheProvider is deprecated in Hibernate 3.3, but LSFB still supports its use public void testLocalSessionFactoryBeanWithCacheProvider() throws Exception { final CacheProvider cacheProvider = new NoCacheProvider(); - final List invocations = new ArrayList(); + final List invocations = new ArrayList(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration addInputStream(InputStream is) { @@ -143,10 +153,11 @@ public class LocalSessionFactoryBeanTests extends TestCase { }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { assertEquals(LocalCacheProviderProxy.class.getName(), - config.getProperty(Environment.CACHE_PROVIDER)); - assertSame(cacheProvider, LocalSessionFactoryBean.getConfigTimeCacheProvider()); + this.getConfiguration().getProperty(Environment.CACHE_PROVIDER)); + assertSame(cacheProvider, SessionFactoryBeanDelegate.getConfigTimeCacheProvider()); invocations.add("newSessionFactory"); return null; } @@ -161,6 +172,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { final DriverManagerDataSource ds = new DriverManagerDataSource(); final List invocations = new ArrayList(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration addInputStream(InputStream is) { @@ -175,10 +187,11 @@ public class LocalSessionFactoryBeanTests extends TestCase { }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { assertEquals(TransactionAwareDataSourceConnectionProvider.class.getName(), - config.getProperty(Environment.CONNECTION_PROVIDER)); - assertEquals(ds, LocalSessionFactoryBean.getConfigTimeDataSource()); + this.getConfiguration().getProperty(Environment.CONNECTION_PROVIDER)); + assertEquals(ds, SessionFactoryBuilderSupport.getConfigTimeDataSource()); invocations.add("newSessionFactory"); return null; } @@ -196,6 +209,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { final TransactionManager tm = (TransactionManager) tmControl.getMock(); final List invocations = new ArrayList(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration addInputStream(InputStream is) { @@ -210,13 +224,14 @@ public class LocalSessionFactoryBeanTests extends TestCase { }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { assertEquals(LocalJtaDataSourceConnectionProvider.class.getName(), - config.getProperty(Environment.CONNECTION_PROVIDER)); - assertEquals(ds, LocalSessionFactoryBean.getConfigTimeDataSource()); + this.getConfiguration().getProperty(Environment.CONNECTION_PROVIDER)); + assertEquals(ds, SessionFactoryBuilderSupport.getConfigTimeDataSource()); assertEquals(LocalTransactionManagerLookup.class.getName(), - config.getProperty(Environment.TRANSACTION_MANAGER_STRATEGY)); - assertEquals(tm, LocalSessionFactoryBean.getConfigTimeTransactionManager()); + this.getConfiguration().getProperty(Environment.TRANSACTION_MANAGER_STRATEGY)); + assertEquals(tm, SessionFactoryBuilderSupport.getConfigTimeTransactionManager()); invocations.add("newSessionFactory"); return null; } @@ -237,6 +252,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { final DriverManagerDataSource ds = new DriverManagerDataSource(); final Set invocations = new HashSet(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration addJar(File file) { @@ -246,10 +262,11 @@ public class LocalSessionFactoryBeanTests extends TestCase { }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { assertEquals(LocalDataSourceConnectionProvider.class.getName(), - config.getProperty(Environment.CONNECTION_PROVIDER)); - assertEquals(ds, LocalSessionFactoryBean.getConfigTimeDataSource()); + this.getConfiguration().getProperty(Environment.CONNECTION_PROVIDER)); + assertEquals(ds, SessionFactoryBuilderSupport.getConfigTimeDataSource()); invocations.add("newSessionFactory"); return null; } @@ -268,6 +285,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { final DriverManagerDataSource ds = new DriverManagerDataSource(); final Set invocations = new HashSet(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration addInputStream(InputStream is) { @@ -282,11 +300,12 @@ public class LocalSessionFactoryBeanTests extends TestCase { }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { assertEquals(LocalDataSourceConnectionProvider.class.getName(), - config.getProperty(Environment.CONNECTION_PROVIDER)); - assertEquals(ds, LocalSessionFactoryBean.getConfigTimeDataSource()); - assertEquals("myValue", config.getProperty("myProperty")); + this.getConfiguration().getProperty(Environment.CONNECTION_PROVIDER)); + assertEquals(ds, SessionFactoryBuilderSupport.getConfigTimeDataSource()); + assertEquals("myValue", this.getConfiguration().getProperty("myProperty")); invocations.add("newSessionFactory"); return null; } @@ -307,10 +326,11 @@ public class LocalSessionFactoryBeanTests extends TestCase { public void testLocalSessionFactoryBeanWithValidProperties() throws Exception { final Set invocations = new HashSet(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { assertEquals(UserSuppliedConnectionProvider.class.getName(), - config.getProperty(Environment.CONNECTION_PROVIDER)); - assertEquals("myValue", config.getProperty("myProperty")); + this.getConfiguration().getProperty(Environment.CONNECTION_PROVIDER)); + assertEquals("myValue", this.getConfiguration().getProperty("myProperty")); invocations.add("newSessionFactory"); return null; } @@ -356,7 +376,8 @@ public class LocalSessionFactoryBeanTests extends TestCase { factoryControl.setVoidCallable(1); factoryControl.replay(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { return sessionFactory; } }; @@ -371,6 +392,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { public void testLocalSessionFactoryBeanWithEntityInterceptor() throws Exception { LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration setInterceptor(Interceptor interceptor) { @@ -397,6 +419,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { public void testLocalSessionFactoryBeanWithNamingStrategy() throws Exception { LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration setNamingStrategy(NamingStrategy namingStrategy) { @@ -422,6 +445,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { final Properties registeredClassCache = new Properties(); final Properties registeredCollectionCache = new Properties(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public Configuration setCacheConcurrencyStrategy(String clazz, String concurrencyStrategy) { @@ -434,7 +458,8 @@ public class LocalSessionFactoryBeanTests extends TestCase { } }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { return null; } }; @@ -457,6 +482,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { final Properties registeredClassCache = new Properties(); final Properties registeredCollectionCache = new Properties(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { // changed from return type 'void' to 'Configuration' in Hibernate 3.6 @@ -468,7 +494,8 @@ public class LocalSessionFactoryBeanTests extends TestCase { } }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { return null; } }; @@ -490,6 +517,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { public void testLocalSessionFactoryBeanWithEventListeners() throws Exception { final Map registeredListeners = new HashMap(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public void setListener(String type, Object listener) { @@ -497,7 +525,8 @@ public class LocalSessionFactoryBeanTests extends TestCase { } }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { return null; } }; @@ -514,6 +543,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { public void testLocalSessionFactoryBeanWithEventListenerSet() throws Exception { final Map registeredListeners = new HashMap(); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + @Override protected Configuration newConfiguration() { return new Configuration() { public void setListeners(String type, Object[] listeners) { @@ -522,7 +552,8 @@ public class LocalSessionFactoryBeanTests extends TestCase { } }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { return null; } }; @@ -579,6 +610,7 @@ public class LocalSessionFactoryBeanTests extends TestCase { public List registeredFilterDefinitions = new LinkedList(); + @Override protected Configuration newConfiguration() throws HibernateException { return new Configuration() { public void addFilterDefinition(FilterDefinition definition) { @@ -587,7 +619,8 @@ public class LocalSessionFactoryBeanTests extends TestCase { }; } - protected SessionFactory newSessionFactory(Configuration config) { + @Override + protected SessionFactory newSessionFactory() { return null; } } @@ -597,8 +630,9 @@ public class LocalSessionFactoryBeanTests extends TestCase { public Mappings mappings; - protected SessionFactory newSessionFactory(Configuration config) { - this.mappings = config.createMappings(); + @Override + protected SessionFactory newSessionFactory() { + this.mappings = this.getConfiguration().createMappings(); return null; } }