Skip to content

Commit 12c1f47

Browse files
committed
extended LocalSessionFactoryBean options for Hibernate 4; factored out LocalSessionFactoryBuilder
1 parent d09faab commit 12c1f47

File tree

2 files changed

+368
-96
lines changed

2 files changed

+368
-96
lines changed

org.springframework.orm/src/main/java/org/springframework/orm/hibernate4/LocalSessionFactoryBean.java

Lines changed: 182 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,23 @@
1616

1717
package org.springframework.orm.hibernate4;
1818

19+
import java.io.File;
1920
import java.io.IOException;
20-
import java.lang.reflect.Method;
2121
import java.util.Properties;
22-
import javax.persistence.Embeddable;
23-
import javax.persistence.Entity;
24-
import javax.persistence.MappedSuperclass;
2522
import javax.sql.DataSource;
2623

27-
import org.hibernate.MappingException;
2824
import org.hibernate.SessionFactory;
29-
import org.hibernate.cfg.Configuration;
30-
import org.hibernate.cfg.Environment;
3125

3226
import org.springframework.beans.factory.DisposableBean;
3327
import org.springframework.beans.factory.FactoryBean;
3428
import org.springframework.beans.factory.InitializingBean;
3529
import org.springframework.context.ResourceLoaderAware;
30+
import org.springframework.core.io.ClassPathResource;
3631
import org.springframework.core.io.Resource;
3732
import org.springframework.core.io.ResourceLoader;
3833
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
3934
import org.springframework.core.io.support.ResourcePatternResolver;
4035
import org.springframework.core.io.support.ResourcePatternUtils;
41-
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
42-
import org.springframework.core.type.classreading.MetadataReader;
43-
import org.springframework.core.type.classreading.MetadataReaderFactory;
44-
import org.springframework.core.type.filter.AnnotationTypeFilter;
45-
import org.springframework.core.type.filter.TypeFilter;
46-
import org.springframework.util.ClassUtils;
47-
import org.springframework.util.ReflectionUtils;
4836

4937
/**
5038
* {@link org.springframework.beans.factory.FactoryBean} that creates a
@@ -66,26 +54,29 @@
6654
public class LocalSessionFactoryBean implements FactoryBean<SessionFactory>, ResourceLoaderAware,
6755
InitializingBean, DisposableBean {
6856

69-
private static final String RESOURCE_PATTERN = "/**/*.class";
57+
private DataSource dataSource;
7058

71-
private static final Method addAnnotatedClassMethod =
72-
ClassUtils.getMethodIfAvailable(Configuration.class, "addAnnotatedClass", Class.class);
59+
private Resource[] configLocations;
7360

61+
private String[] mappingResources;
7462

75-
private DataSource dataSource;
63+
private Resource[] mappingLocations;
7664

77-
private Properties hibernateProperties;
65+
private Resource[] cacheableMappingLocations;
7866

79-
private String[] packagesToScan;
67+
private Resource[] mappingJarLocations;
68+
69+
private Resource[] mappingDirectoryLocations;
70+
71+
private Properties hibernateProperties;
8072

8173
private Class<?>[] annotatedClasses;
8274

83-
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
75+
private String[] annotatedPackages;
8476

85-
private TypeFilter[] entityTypeFilters = new TypeFilter[] {
86-
new AnnotationTypeFilter(Entity.class, false),
87-
new AnnotationTypeFilter(Embeddable.class, false),
88-
new AnnotationTypeFilter(MappedSuperclass.class, false)};
77+
private String[] packagesToScan;
78+
79+
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
8980

9081
private SessionFactory sessionFactory;
9182

@@ -95,28 +86,97 @@ public class LocalSessionFactoryBean implements FactoryBean<SessionFactory>, Res
9586
* If set, this will override corresponding settings in Hibernate properties.
9687
* <p>If this is set, the Hibernate settings should not define
9788
* a connection provider to avoid meaningless double configuration.
98-
* <p>If using HibernateTransactionManager as transaction strategy, consider
99-
* proxying your target DataSource with a LazyConnectionDataSourceProxy.
100-
* This defers fetching of an actual JDBC Connection until the first JDBC
101-
* Statement gets executed, even within JDBC transactions (as performed by
102-
* HibernateTransactionManager). Such lazy fetching is particularly beneficial
103-
* for read-only operations, in particular if the chances of resolving the
104-
* result in the second-level cache are high.
105-
* <p>As JTA and transactional JNDI DataSources already provide lazy enlistment
106-
* of JDBC Connections, LazyConnectionDataSourceProxy does not add value with
107-
* JTA (i.e. Spring's JtaTransactionManager) as transaction strategy.
108-
* @see HibernateTransactionManager
109-
* @see org.springframework.transaction.jta.JtaTransactionManager
110-
* @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
11189
*/
11290
public void setDataSource(DataSource dataSource) {
11391
this.dataSource = dataSource;
11492
}
11593

94+
/**
95+
* Set the location of a single Hibernate XML config file, for example as
96+
* classpath resource "classpath:hibernate.cfg.xml".
97+
* <p>Note: Can be omitted when all necessary properties and mapping
98+
* resources are specified locally via this bean.
99+
* @see org.hibernate.cfg.Configuration#configure(java.net.URL)
100+
*/
101+
public void setConfigLocation(Resource configLocation) {
102+
this.configLocations = new Resource[] {configLocation};
103+
}
104+
105+
/**
106+
* Set the locations of multiple Hibernate XML config files, for example as
107+
* classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml".
108+
* <p>Note: Can be omitted when all necessary properties and mapping
109+
* resources are specified locally via this bean.
110+
* @see org.hibernate.cfg.Configuration#configure(java.net.URL)
111+
*/
112+
public void setConfigLocations(Resource[] configLocations) {
113+
this.configLocations = configLocations;
114+
}
115+
116+
/**
117+
* Set Hibernate mapping resources to be found in the class path,
118+
* like "example.hbm.xml" or "mypackage/example.hbm.xml".
119+
* Analogous to mapping entries in a Hibernate XML config file.
120+
* Alternative to the more generic setMappingLocations method.
121+
* <p>Can be used to add to mappings from a Hibernate XML config file,
122+
* or to specify all mappings locally.
123+
* @see #setMappingLocations
124+
* @see org.hibernate.cfg.Configuration#addResource
125+
*/
126+
public void setMappingResources(String[] mappingResources) {
127+
this.mappingResources = mappingResources;
128+
}
129+
130+
/**
131+
* Set locations of Hibernate mapping files, for example as classpath
132+
* resource "classpath:example.hbm.xml". Supports any resource location
133+
* via Spring's resource abstraction, for example relative paths like
134+
* "WEB-INF/mappings/example.hbm.xml" when running in an application context.
135+
* <p>Can be used to add to mappings from a Hibernate XML config file,
136+
* or to specify all mappings locally.
137+
* @see org.hibernate.cfg.Configuration#addInputStream
138+
*/
139+
public void setMappingLocations(Resource[] mappingLocations) {
140+
this.mappingLocations = mappingLocations;
141+
}
142+
143+
/**
144+
* Set locations of cacheable Hibernate mapping files, for example as web app
145+
* resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location
146+
* via Spring's resource abstraction, as long as the resource can be resolved
147+
* in the file system.
148+
* <p>Can be used to add to mappings from a Hibernate XML config file,
149+
* or to specify all mappings locally.
150+
* @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File)
151+
*/
152+
public void setCacheableMappingLocations(Resource[] cacheableMappingLocations) {
153+
this.cacheableMappingLocations = cacheableMappingLocations;
154+
}
155+
156+
/**
157+
* Set locations of jar files that contain Hibernate mapping resources,
158+
* like "WEB-INF/lib/example.hbm.jar".
159+
* <p>Can be used to add to mappings from a Hibernate XML config file,
160+
* or to specify all mappings locally.
161+
* @see org.hibernate.cfg.Configuration#addJar(java.io.File)
162+
*/
163+
public void setMappingJarLocations(Resource[] mappingJarLocations) {
164+
this.mappingJarLocations = mappingJarLocations;
165+
}
166+
167+
/**
168+
* Set locations of directories that contain Hibernate mapping resources,
169+
* like "WEB-INF/mappings".
170+
* <p>Can be used to add to mappings from a Hibernate XML config file,
171+
* or to specify all mappings locally.
172+
* @see org.hibernate.cfg.Configuration#addDirectory(java.io.File)
173+
*/
174+
public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) {
175+
this.mappingDirectoryLocations = mappingDirectoryLocations;
176+
}
177+
116178
/**
117179
* Set Hibernate properties, such as "hibernate.dialect".
118-
* <p>Can be used to override values in a Hibernate XML config file,
119-
* or to specify all necessary properties locally.
120180
* <p>Note: Do not specify a transaction provider here when using
121181
* Spring-driven transactions. It is also advisable to omit connection
122182
* provider settings and use a Spring-set DataSource instead.
@@ -127,87 +187,113 @@ public void setHibernateProperties(Properties hibernateProperties) {
127187
}
128188

129189
/**
130-
* Specify packages to search for autodetection of your entity classes in the
131-
* classpath. This is analogous to Spring's component-scan feature
132-
* ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
190+
* Return the Hibernate properties, if any. Mainly available for
191+
* configuration through property paths that specify individual keys.
133192
*/
134-
public void setPackagesToScan(String... packagesToScan) {
135-
this.packagesToScan = packagesToScan;
193+
public Properties getHibernateProperties() {
194+
if (this.hibernateProperties == null) {
195+
this.hibernateProperties = new Properties();
196+
}
197+
return this.hibernateProperties;
136198
}
137199

138200
/**
139201
* Specify annotated entity classes to register with this Hibernate SessionFactory.
202+
* @see org.hibernate.cfg.Configuration#addAnnotatedClass(String)
140203
*/
141-
public void setAnnotatedClasses(Class<?>... annotatedClasses) {
204+
public void setAnnotatedClasses(Class<?>[] annotatedClasses) {
142205
this.annotatedClasses = annotatedClasses;
143206
}
144207

208+
/**
209+
* Specify the names of annotated packages, for which package-level
210+
* annotation metadata will be read.
211+
* @see org.hibernate.cfg.Configuration#addPackage(String)
212+
*/
213+
public void setAnnotatedPackages(String[] annotatedPackages) {
214+
this.annotatedPackages = annotatedPackages;
215+
}
216+
217+
/**
218+
* Specify packages to search for autodetection of your entity classes in the
219+
* classpath. This is analogous to Spring's component-scan feature
220+
* ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
221+
*/
222+
public void setPackagesToScan(String... packagesToScan) {
223+
this.packagesToScan = packagesToScan;
224+
}
225+
145226
public void setResourceLoader(ResourceLoader resourceLoader) {
146227
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
147228
}
148229

149230

150-
public void afterPropertiesSet() {
151-
Configuration config = new Configuration();
152-
config.getProperties().put(Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName());
153-
config.getProperties().put(Environment.DATASOURCE, this.dataSource);
154-
config.getProperties().put("hibernate.classLoader.application", this.resourcePatternResolver.getClassLoader());
155-
if (this.hibernateProperties != null) {
156-
config.addProperties(this.hibernateProperties);
231+
public void afterPropertiesSet() throws IOException {
232+
LocalSessionFactoryBuilder sfb = new LocalSessionFactoryBuilder(this.dataSource, this.resourcePatternResolver);
233+
234+
if (this.configLocations != null) {
235+
for (Resource resource : this.configLocations) {
236+
// Load Hibernate configuration from given location.
237+
sfb.configure(resource.getURL());
238+
}
157239
}
158-
scanPackages(config);
159-
for (Class<?> annotatedClass : this.annotatedClasses) {
160-
ReflectionUtils.invokeMethod(addAnnotatedClassMethod, config, annotatedClass);
240+
241+
if (this.mappingResources != null) {
242+
// Register given Hibernate mapping definitions, contained in resource files.
243+
for (String mapping : this.mappingResources) {
244+
Resource mr = new ClassPathResource(mapping.trim(), this.resourcePatternResolver.getClassLoader());
245+
sfb.addInputStream(mr.getInputStream());
246+
}
161247
}
162-
this.sessionFactory = config.buildSessionFactory();
163-
}
164248

165-
/**
166-
* Perform Spring-based scanning for entity classes.
167-
* @see #setPackagesToScan
168-
*/
169-
private void scanPackages(Configuration config) {
170-
if (this.packagesToScan != null) {
171-
try {
172-
for (String pkg : this.packagesToScan) {
173-
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
174-
ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;
175-
Resource[] resources = this.resourcePatternResolver.getResources(pattern);
176-
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
177-
for (Resource resource : resources) {
178-
if (resource.isReadable()) {
179-
MetadataReader reader = readerFactory.getMetadataReader(resource);
180-
String className = reader.getClassMetadata().getClassName();
181-
if (matchesFilter(reader, readerFactory)) {
182-
Class<?> annotatedClass = this.resourcePatternResolver.getClassLoader().loadClass(className);
183-
ReflectionUtils.invokeMethod(addAnnotatedClassMethod, config, annotatedClass);
184-
}
185-
}
186-
}
187-
}
249+
if (this.mappingLocations != null) {
250+
// Register given Hibernate mapping definitions, contained in resource files.
251+
for (Resource resource : this.mappingLocations) {
252+
sfb.addInputStream(resource.getInputStream());
188253
}
189-
catch (IOException ex) {
190-
throw new MappingException("Failed to scan classpath for unlisted classes", ex);
254+
}
255+
256+
if (this.cacheableMappingLocations != null) {
257+
// Register given cacheable Hibernate mapping definitions, read from the file system.
258+
for (Resource resource : this.cacheableMappingLocations) {
259+
sfb.addCacheableFile(resource.getFile());
191260
}
192-
catch (ClassNotFoundException ex) {
193-
throw new MappingException("Failed to load annotated classes from classpath", ex);
261+
}
262+
263+
if (this.mappingJarLocations != null) {
264+
// Register given Hibernate mapping definitions, contained in jar files.
265+
for (Resource resource : this.mappingJarLocations) {
266+
sfb.addJar(resource.getFile());
194267
}
195268
}
196-
}
197269

198-
/**
199-
* Check whether any of the configured entity type filters matches
200-
* the current class descriptor contained in the metadata reader.
201-
*/
202-
private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
203-
if (this.entityTypeFilters != null) {
204-
for (TypeFilter filter : this.entityTypeFilters) {
205-
if (filter.match(reader, readerFactory)) {
206-
return true;
270+
if (this.mappingDirectoryLocations != null) {
271+
// Register all Hibernate mapping definitions in the given directories.
272+
for (Resource resource : this.mappingDirectoryLocations) {
273+
File file = resource.getFile();
274+
if (!file.isDirectory()) {
275+
throw new IllegalArgumentException(
276+
"Mapping directory location [" + resource + "] does not denote a directory");
207277
}
278+
sfb.addDirectory(file);
208279
}
209280
}
210-
return false;
281+
282+
if (this.hibernateProperties != null) {
283+
sfb.addProperties(this.hibernateProperties);
284+
}
285+
286+
if (this.annotatedClasses != null) {
287+
sfb.addAnnotatedClasses(this.annotatedClasses);
288+
}
289+
290+
if (this.annotatedPackages != null) {
291+
sfb.addPackages(this.annotatedPackages);
292+
}
293+
294+
sfb.scanPackages(this.packagesToScan);
295+
296+
this.sessionFactory = sfb.buildSessionFactory();
211297
}
212298

213299

0 commit comments

Comments
 (0)