From eb0b4f0cbd6d981e6864d4f02bc04b678be4cd1c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 19 Nov 2009 15:39:11 +0000 Subject: [PATCH] added support for Hibernate 3.3 RegionFactory cache SPI to LocalSessionFactoryBean (SPR-6387) --- .../hibernate3/LocalCacheProviderProxy.java | 2 +- .../hibernate3/LocalRegionFactoryProxy.java | 95 +++++++++++++++ .../hibernate3/LocalSessionFactoryBean.java | 110 +++++++++++++----- .../LocalSessionFactoryBeanTests.java | 35 +++++- 4 files changed, 210 insertions(+), 32 deletions(-) create mode 100644 org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalRegionFactoryProxy.java 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 a61273740a0..e240800e0f3 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 @@ -29,7 +29,7 @@ import org.hibernate.cache.CacheProvider; * * @author Juergen Hoeller * @since 2.5.1 - * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setCacheProvider + * @see LocalSessionFactoryBean#setCacheProvider */ public class LocalCacheProviderProxy implements CacheProvider { 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 new file mode 100644 index 00000000000..1f52c6b7fdb --- /dev/null +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalRegionFactoryProxy.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2009 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.util.Properties; + +import org.hibernate.cache.CacheDataDescription; +import org.hibernate.cache.CacheException; +import org.hibernate.cache.CollectionRegion; +import org.hibernate.cache.EntityRegion; +import org.hibernate.cache.QueryResultsRegion; +import org.hibernate.cache.RegionFactory; +import org.hibernate.cache.TimestampsRegion; +import org.hibernate.cfg.Settings; + +/** + * Proxy for a Hibernate RegionFactory, delegating to a Spring-managed + * RegionFactory instance, determined by LocalSessionFactoryBean's + * "cacheRegionFactory" property. + * + * @author Juergen Hoeller + * @since 3.0 + * @see LocalSessionFactoryBean#setCacheRegionFactory + */ +public class LocalRegionFactoryProxy implements RegionFactory { + + private final RegionFactory regionFactory; + + + public LocalRegionFactoryProxy() { + RegionFactory rf = LocalSessionFactoryBean.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"); + } + this.regionFactory = rf; + } + + + public void start(Settings settings, Properties properties) throws CacheException { + this.regionFactory.start(settings, properties); + } + + public void stop() { + this.regionFactory.stop(); + } + + public boolean isMinimalPutsEnabledByDefault() { + return this.regionFactory.isMinimalPutsEnabledByDefault(); + } + + public long nextTimestamp() { + return this.regionFactory.nextTimestamp(); + } + + public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) + throws CacheException { + + return this.regionFactory.buildEntityRegion(regionName, properties, metadata); + } + + public CollectionRegion buildCollectionRegion(String regionName, Properties properties, + CacheDataDescription metadata) throws CacheException { + + return this.regionFactory.buildCollectionRegion(regionName, properties, metadata); + } + + public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) + throws CacheException { + + return this.regionFactory.buildQueryResultsRegion(regionName, properties); + } + + public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) + throws CacheException { + + return this.regionFactory.buildTimestampsRegion(regionName, properties); + } + +} 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 ac77e4a9ab3..19fb9a08b40 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-2008 the original author or authors. + * Copyright 2002-2009 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. @@ -34,12 +34,14 @@ import org.hibernate.Interceptor; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cache.CacheProvider; +import org.hibernate.cache.RegionFactory; 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.jdbc.Work; import org.hibernate.tool.hbm2ddl.DatabaseMetadata; import org.hibernate.transaction.JTATransactionFactory; @@ -88,7 +90,7 @@ import org.springframework.util.StringUtils; * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter} / * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}. * - *

Requires Hibernate 3.2 or later. Note that this factory will use + *

Requires Hibernate 3.3 or later. 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 @@ -113,6 +115,9 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen private static final ThreadLocal configTimeTransactionManagerHolder = new ThreadLocal(); + private static final ThreadLocal configTimeRegionFactoryHolder = + new ThreadLocal(); + private static final ThreadLocal configTimeCacheProviderHolder = new ThreadLocal(); @@ -145,6 +150,18 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen 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 + */ + public static RegionFactory getConfigTimeRegionFactory() { + return configTimeRegionFactoryHolder.get(); + } + /** * Return the CacheProvider for the currently configured Hibernate SessionFactory, * to be used by LocalCacheProviderProxy. @@ -173,7 +190,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen } - private Class configurationClass = Configuration.class; + private Class configurationClass = Configuration.class; private Resource[] configLocations; @@ -191,6 +208,8 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen private TransactionManager jtaTransactionManager; + private RegionFactory cacheRegionFactory; + private CacheProvider cacheProvider; private LobHandler lobHandler; @@ -231,12 +250,13 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen * @see org.hibernate.cfg.Configuration * @see org.hibernate.cfg.AnnotationConfiguration */ - public void setConfigurationClass(Class configurationClass) { + @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]"); + "'configurationClass' must be assignable to [org.hibernate.cfg.Configuration]"); } - this.configurationClass = configurationClass; + this.configurationClass = (Class) configurationClass; } /** @@ -359,13 +379,29 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen this.jtaTransactionManager = jtaTransactionManager; } + /** + * 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}. + *

Note: If this is set, the Hibernate settings should not define a + * cache provider to avoid meaningless double configuration. + * @see LocalRegionFactoryProxy + */ + public void setCacheRegionFactory(RegionFactory cacheRegionFactory) { + this.cacheRegionFactory = cacheRegionFactory; + } + /** * 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. - * @see LocalCacheProviderProxy + * @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; } @@ -523,6 +559,10 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen // 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); @@ -622,7 +662,11 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen config.setProperty(Environment.CONNECTION_PROVIDER, providerClass.getName()); } - if (this.cacheProvider != null) { + if (this.cacheRegionFactory != null) { + // Expose Spring-provided Hibernate RegionFactory. + config.setProperty(Environment.CACHE_REGION_FACTORY, LocalRegionFactoryProxy.class.getName()); + } + else if (this.cacheProvider != null) { // Expose Spring-provided Hibernate CacheProvider. config.setProperty(Environment.CACHE_PROVIDER, LocalCacheProviderProxy.class.getName()); } @@ -733,19 +777,18 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen finally { if (dataSource != null) { - // Reset DataSource holder. configTimeDataSourceHolder.set(null); } if (this.jtaTransactionManager != null) { - // Reset TransactionManager holder. configTimeTransactionManagerHolder.set(null); } + if (this.cacheRegionFactory != null) { + configTimeCacheProviderHolder.set(null); + } if (this.cacheProvider != null) { - // Reset CacheProvider holder. configTimeCacheProviderHolder.set(null); } if (this.lobHandler != null) { - // Reset LobHandler holder. configTimeLobHandlerHolder.set(null); } if (overrideClassLoader) { @@ -768,7 +811,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen * @see org.hibernate.cfg.Configuration#Configuration() */ protected Configuration newConfiguration() throws HibernateException { - return (Configuration) BeanUtils.instantiateClass(this.configurationClass); + return BeanUtils.instantiateClass(this.configurationClass); } /** @@ -891,12 +934,15 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen logger.info("Dropping database schema for Hibernate SessionFactory"); HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); hibernateTemplate.execute( - new HibernateCallback() { + 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); + session.doWork(new Work() { + public void execute(Connection connection) throws SQLException { + Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); + String[] sql = getConfiguration().generateDropSchemaScript(dialect); + executeSchemaScript(connection, sql); + } + }); return null; } } @@ -920,12 +966,15 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen logger.info("Creating database schema for Hibernate SessionFactory"); HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); hibernateTemplate.execute( - new HibernateCallback() { + 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); + session.doWork(new Work() { + public void execute(Connection connection) throws SQLException { + Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); + String[] sql = getConfiguration().generateSchemaCreationScript(dialect); + executeSchemaScript(connection, sql); + } + }); return null; } } @@ -952,13 +1001,16 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); hibernateTemplate.execute( - new HibernateCallback() { + 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); + session.doWork(new Work() { + public void execute(Connection connection) throws SQLException { + Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); + DatabaseMetadata metadata = new DatabaseMetadata(connection, dialect); + String[] sql = getConfiguration().generateSchemaUpdateScript(dialect, metadata); + executeSchemaScript(connection, sql); + } + }); return null; } } 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 73286384b03..f2a2685a1e1 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,7 +28,6 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; - import javax.transaction.TransactionManager; import junit.framework.TestCase; @@ -39,6 +38,8 @@ import org.hibernate.Interceptor; import org.hibernate.SessionFactory; import org.hibernate.cache.CacheProvider; import org.hibernate.cache.NoCacheProvider; +import org.hibernate.cache.RegionFactory; +import org.hibernate.cache.impl.NoCachingRegionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.cfg.ImprovedNamingStrategy; @@ -79,7 +80,6 @@ public class LocalSessionFactoryBeanTests extends TestCase { } }; } - protected SessionFactory newSessionFactory(Configuration config) { assertEquals(LocalDataSourceConnectionProvider.class.getName(), config.getProperty(Environment.CONNECTION_PROVIDER)); @@ -94,6 +94,37 @@ public class LocalSessionFactoryBeanTests extends TestCase { assertEquals("newSessionFactory", invocations.get(0)); } + public void testLocalSessionFactoryBeanWithCacheRegionFactory() throws Exception { + final RegionFactory regionFactory = new NoCachingRegionFactory(null); + final List invocations = new ArrayList(); + LocalSessionFactoryBean sfb = new LocalSessionFactoryBean() { + protected Configuration newConfiguration() { + return new Configuration() { + public Configuration addInputStream(InputStream is) { + try { + is.close(); + } + catch (IOException ex) { + } + invocations.add("addResource"); + return this; + } + }; + } + protected SessionFactory newSessionFactory(Configuration config) { + assertEquals(LocalRegionFactoryProxy.class.getName(), + config.getProperty(Environment.CACHE_REGION_FACTORY)); + assertSame(regionFactory, LocalSessionFactoryBean.getConfigTimeRegionFactory()); + invocations.add("newSessionFactory"); + return null; + } + }; + sfb.setCacheRegionFactory(regionFactory); + sfb.afterPropertiesSet(); + assertTrue(sfb.getConfiguration() != null); + assertEquals("newSessionFactory", invocations.get(0)); + } + public void testLocalSessionFactoryBeanWithCacheProvider() throws Exception { final CacheProvider cacheProvider = new NoCacheProvider(); final List invocations = new ArrayList();