added support for Hibernate 3.3 RegionFactory cache SPI to LocalSessionFactoryBean (SPR-6387)

This commit is contained in:
Juergen Hoeller 2009-11-19 15:39:11 +00:00
parent f2477c4bf5
commit eb0b4f0cbd
4 changed files with 210 additions and 32 deletions

View File

@ -29,7 +29,7 @@ import org.hibernate.cache.CacheProvider;
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 2.5.1 * @since 2.5.1
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setCacheProvider * @see LocalSessionFactoryBean#setCacheProvider
*/ */
public class LocalCacheProviderProxy implements CacheProvider { public class LocalCacheProviderProxy implements CacheProvider {

View File

@ -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);
}
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Session;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.cache.CacheProvider; import org.hibernate.cache.CacheProvider;
import org.hibernate.cache.RegionFactory;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.NamingStrategy;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FilterDefinition; import org.hibernate.engine.FilterDefinition;
import org.hibernate.event.EventListeners; import org.hibernate.event.EventListeners;
import org.hibernate.jdbc.Work;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata; import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
import org.hibernate.transaction.JTATransactionFactory; 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.OpenSessionInViewFilter} /
* {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}. * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}.
* *
* <p><b>Requires Hibernate 3.2 or later.</b> Note that this factory will use * <p><b>Requires Hibernate 3.3 or later.</b> Note that this factory will use
* "on_close" as default Hibernate connection release mode, unless in the * "on_close" as default Hibernate connection release mode, unless in the
* case of a "jtaTransactionManager" specified, for the reason that * case of a "jtaTransactionManager" specified, for the reason that
* this is appropriate for most Spring-based applications (in particular when * 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<TransactionManager> configTimeTransactionManagerHolder = private static final ThreadLocal<TransactionManager> configTimeTransactionManagerHolder =
new ThreadLocal<TransactionManager>(); new ThreadLocal<TransactionManager>();
private static final ThreadLocal<RegionFactory> configTimeRegionFactoryHolder =
new ThreadLocal<RegionFactory>();
private static final ThreadLocal<CacheProvider> configTimeCacheProviderHolder = private static final ThreadLocal<CacheProvider> configTimeCacheProviderHolder =
new ThreadLocal<CacheProvider>(); new ThreadLocal<CacheProvider>();
@ -145,6 +150,18 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
return configTimeTransactionManagerHolder.get(); return configTimeTransactionManagerHolder.get();
} }
/**
* Return the RegionFactory for the currently configured Hibernate SessionFactory,
* to be used by LocalRegionFactoryProxy.
* <p>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, * Return the CacheProvider for the currently configured Hibernate SessionFactory,
* to be used by LocalCacheProviderProxy. * to be used by LocalCacheProviderProxy.
@ -173,7 +190,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
} }
private Class configurationClass = Configuration.class; private Class<? extends Configuration> configurationClass = Configuration.class;
private Resource[] configLocations; private Resource[] configLocations;
@ -191,6 +208,8 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
private TransactionManager jtaTransactionManager; private TransactionManager jtaTransactionManager;
private RegionFactory cacheRegionFactory;
private CacheProvider cacheProvider; private CacheProvider cacheProvider;
private LobHandler lobHandler; private LobHandler lobHandler;
@ -231,12 +250,13 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
* @see org.hibernate.cfg.Configuration * @see org.hibernate.cfg.Configuration
* @see org.hibernate.cfg.AnnotationConfiguration * @see org.hibernate.cfg.AnnotationConfiguration
*/ */
public void setConfigurationClass(Class configurationClass) { @SuppressWarnings("unchecked")
public void setConfigurationClass(Class<?> configurationClass) {
if (configurationClass == null || !Configuration.class.isAssignableFrom(configurationClass)) { if (configurationClass == null || !Configuration.class.isAssignableFrom(configurationClass)) {
throw new IllegalArgumentException( 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<? extends Configuration>) configurationClass;
} }
/** /**
@ -359,13 +379,29 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
this.jtaTransactionManager = jtaTransactionManager; this.jtaTransactionManager = jtaTransactionManager;
} }
/**
* Set the Hibernate RegionFactory to use for the SessionFactory.
* Allows for using a Spring-managed RegionFactory instance.
* <p>As of Hibernate 3.3, this is the preferred mechanism for configuring
* caches, superseding the {@link #setCacheProvider CacheProvider SPI}.
* <p>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. * Set the Hibernate CacheProvider to use for the SessionFactory.
* Allows for using a Spring-managed CacheProvider instance. * Allows for using a Spring-managed CacheProvider instance.
* <p>Note: If this is set, the Hibernate settings should not define a * <p>Note: If this is set, the Hibernate settings should not define a
* cache provider to avoid meaningless double configuration. * 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) { public void setCacheProvider(CacheProvider cacheProvider) {
this.cacheProvider = cacheProvider; this.cacheProvider = cacheProvider;
} }
@ -523,6 +559,10 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
// Make Spring-provided JTA TransactionManager available. // Make Spring-provided JTA TransactionManager available.
configTimeTransactionManagerHolder.set(this.jtaTransactionManager); configTimeTransactionManagerHolder.set(this.jtaTransactionManager);
} }
if (this.cacheRegionFactory != null) {
// Make Spring-provided Hibernate RegionFactory available.
configTimeRegionFactoryHolder.set(this.cacheRegionFactory);
}
if (this.cacheProvider != null) { if (this.cacheProvider != null) {
// Make Spring-provided Hibernate CacheProvider available. // Make Spring-provided Hibernate CacheProvider available.
configTimeCacheProviderHolder.set(this.cacheProvider); configTimeCacheProviderHolder.set(this.cacheProvider);
@ -622,7 +662,11 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
config.setProperty(Environment.CONNECTION_PROVIDER, providerClass.getName()); 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. // Expose Spring-provided Hibernate CacheProvider.
config.setProperty(Environment.CACHE_PROVIDER, LocalCacheProviderProxy.class.getName()); config.setProperty(Environment.CACHE_PROVIDER, LocalCacheProviderProxy.class.getName());
} }
@ -733,19 +777,18 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
finally { finally {
if (dataSource != null) { if (dataSource != null) {
// Reset DataSource holder.
configTimeDataSourceHolder.set(null); configTimeDataSourceHolder.set(null);
} }
if (this.jtaTransactionManager != null) { if (this.jtaTransactionManager != null) {
// Reset TransactionManager holder.
configTimeTransactionManagerHolder.set(null); configTimeTransactionManagerHolder.set(null);
} }
if (this.cacheRegionFactory != null) {
configTimeCacheProviderHolder.set(null);
}
if (this.cacheProvider != null) { if (this.cacheProvider != null) {
// Reset CacheProvider holder.
configTimeCacheProviderHolder.set(null); configTimeCacheProviderHolder.set(null);
} }
if (this.lobHandler != null) { if (this.lobHandler != null) {
// Reset LobHandler holder.
configTimeLobHandlerHolder.set(null); configTimeLobHandlerHolder.set(null);
} }
if (overrideClassLoader) { if (overrideClassLoader) {
@ -768,7 +811,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
* @see org.hibernate.cfg.Configuration#Configuration() * @see org.hibernate.cfg.Configuration#Configuration()
*/ */
protected Configuration newConfiguration() throws HibernateException { 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"); logger.info("Dropping database schema for Hibernate SessionFactory");
HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory());
hibernateTemplate.execute( hibernateTemplate.execute(
new HibernateCallback() { new HibernateCallback<Object>() {
public Object doInHibernate(Session session) throws HibernateException, SQLException { public Object doInHibernate(Session session) throws HibernateException, SQLException {
Connection con = session.connection(); session.doWork(new Work() {
Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); public void execute(Connection connection) throws SQLException {
String[] sql = getConfiguration().generateDropSchemaScript(dialect); Dialect dialect = Dialect.getDialect(getConfiguration().getProperties());
executeSchemaScript(con, sql); String[] sql = getConfiguration().generateDropSchemaScript(dialect);
executeSchemaScript(connection, sql);
}
});
return null; return null;
} }
} }
@ -920,12 +966,15 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
logger.info("Creating database schema for Hibernate SessionFactory"); logger.info("Creating database schema for Hibernate SessionFactory");
HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory());
hibernateTemplate.execute( hibernateTemplate.execute(
new HibernateCallback() { new HibernateCallback<Object>() {
public Object doInHibernate(Session session) throws HibernateException, SQLException { public Object doInHibernate(Session session) throws HibernateException, SQLException {
Connection con = session.connection(); session.doWork(new Work() {
Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); public void execute(Connection connection) throws SQLException {
String[] sql = getConfiguration().generateSchemaCreationScript(dialect); Dialect dialect = Dialect.getDialect(getConfiguration().getProperties());
executeSchemaScript(con, sql); String[] sql = getConfiguration().generateSchemaCreationScript(dialect);
executeSchemaScript(connection, sql);
}
});
return null; return null;
} }
} }
@ -952,13 +1001,16 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory());
hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER);
hibernateTemplate.execute( hibernateTemplate.execute(
new HibernateCallback() { new HibernateCallback<Object>() {
public Object doInHibernate(Session session) throws HibernateException, SQLException { public Object doInHibernate(Session session) throws HibernateException, SQLException {
Connection con = session.connection(); session.doWork(new Work() {
Dialect dialect = Dialect.getDialect(getConfiguration().getProperties()); public void execute(Connection connection) throws SQLException {
DatabaseMetadata metadata = new DatabaseMetadata(con, dialect); Dialect dialect = Dialect.getDialect(getConfiguration().getProperties());
String[] sql = getConfiguration().generateSchemaUpdateScript(dialect, metadata); DatabaseMetadata metadata = new DatabaseMetadata(connection, dialect);
executeSchemaScript(con, sql); String[] sql = getConfiguration().generateSchemaUpdateScript(dialect, metadata);
executeSchemaScript(connection, sql);
}
});
return null; return null;
} }
} }

View File

@ -28,7 +28,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import javax.transaction.TransactionManager; import javax.transaction.TransactionManager;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -39,6 +38,8 @@ import org.hibernate.Interceptor;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.cache.CacheProvider; import org.hibernate.cache.CacheProvider;
import org.hibernate.cache.NoCacheProvider; 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.Configuration;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.cfg.ImprovedNamingStrategy; import org.hibernate.cfg.ImprovedNamingStrategy;
@ -79,7 +80,6 @@ public class LocalSessionFactoryBeanTests extends TestCase {
} }
}; };
} }
protected SessionFactory newSessionFactory(Configuration config) { protected SessionFactory newSessionFactory(Configuration config) {
assertEquals(LocalDataSourceConnectionProvider.class.getName(), assertEquals(LocalDataSourceConnectionProvider.class.getName(),
config.getProperty(Environment.CONNECTION_PROVIDER)); config.getProperty(Environment.CONNECTION_PROVIDER));
@ -94,6 +94,37 @@ public class LocalSessionFactoryBeanTests extends TestCase {
assertEquals("newSessionFactory", invocations.get(0)); 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 { public void testLocalSessionFactoryBeanWithCacheProvider() throws Exception {
final CacheProvider cacheProvider = new NoCacheProvider(); final CacheProvider cacheProvider = new NoCacheProvider();
final List invocations = new ArrayList(); final List invocations = new ArrayList();