16
16
17
17
package org .springframework .orm .hibernate4 ;
18
18
19
+ import java .io .File ;
19
20
import java .io .IOException ;
20
- import java .lang .reflect .Method ;
21
21
import java .util .Properties ;
22
- import javax .persistence .Embeddable ;
23
- import javax .persistence .Entity ;
24
- import javax .persistence .MappedSuperclass ;
25
22
import javax .sql .DataSource ;
26
23
27
- import org .hibernate .MappingException ;
28
24
import org .hibernate .SessionFactory ;
29
- import org .hibernate .cfg .Configuration ;
30
- import org .hibernate .cfg .Environment ;
31
25
32
26
import org .springframework .beans .factory .DisposableBean ;
33
27
import org .springframework .beans .factory .FactoryBean ;
34
28
import org .springframework .beans .factory .InitializingBean ;
35
29
import org .springframework .context .ResourceLoaderAware ;
30
+ import org .springframework .core .io .ClassPathResource ;
36
31
import org .springframework .core .io .Resource ;
37
32
import org .springframework .core .io .ResourceLoader ;
38
33
import org .springframework .core .io .support .PathMatchingResourcePatternResolver ;
39
34
import org .springframework .core .io .support .ResourcePatternResolver ;
40
35
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 ;
48
36
49
37
/**
50
38
* {@link org.springframework.beans.factory.FactoryBean} that creates a
66
54
public class LocalSessionFactoryBean implements FactoryBean <SessionFactory >, ResourceLoaderAware ,
67
55
InitializingBean , DisposableBean {
68
56
69
- private static final String RESOURCE_PATTERN = "/**/*.class" ;
57
+ private DataSource dataSource ;
70
58
71
- private static final Method addAnnotatedClassMethod =
72
- ClassUtils .getMethodIfAvailable (Configuration .class , "addAnnotatedClass" , Class .class );
59
+ private Resource [] configLocations ;
73
60
61
+ private String [] mappingResources ;
74
62
75
- private DataSource dataSource ;
63
+ private Resource [] mappingLocations ;
76
64
77
- private Properties hibernateProperties ;
65
+ private Resource [] cacheableMappingLocations ;
78
66
79
- private String [] packagesToScan ;
67
+ private Resource [] mappingJarLocations ;
68
+
69
+ private Resource [] mappingDirectoryLocations ;
70
+
71
+ private Properties hibernateProperties ;
80
72
81
73
private Class <?>[] annotatedClasses ;
82
74
83
- private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver () ;
75
+ private String [] annotatedPackages ;
84
76
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 ();
89
80
90
81
private SessionFactory sessionFactory ;
91
82
@@ -95,28 +86,97 @@ public class LocalSessionFactoryBean implements FactoryBean<SessionFactory>, Res
95
86
* If set, this will override corresponding settings in Hibernate properties.
96
87
* <p>If this is set, the Hibernate settings should not define
97
88
* 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
111
89
*/
112
90
public void setDataSource (DataSource dataSource ) {
113
91
this .dataSource = dataSource ;
114
92
}
115
93
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
+
116
178
/**
117
179
* 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.
120
180
* <p>Note: Do not specify a transaction provider here when using
121
181
* Spring-driven transactions. It is also advisable to omit connection
122
182
* provider settings and use a Spring-set DataSource instead.
@@ -127,87 +187,113 @@ public void setHibernateProperties(Properties hibernateProperties) {
127
187
}
128
188
129
189
/**
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.
133
192
*/
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 ;
136
198
}
137
199
138
200
/**
139
201
* Specify annotated entity classes to register with this Hibernate SessionFactory.
202
+ * @see org.hibernate.cfg.Configuration#addAnnotatedClass(String)
140
203
*/
141
- public void setAnnotatedClasses (Class <?>... annotatedClasses ) {
204
+ public void setAnnotatedClasses (Class <?>[] annotatedClasses ) {
142
205
this .annotatedClasses = annotatedClasses ;
143
206
}
144
207
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
+
145
226
public void setResourceLoader (ResourceLoader resourceLoader ) {
146
227
this .resourcePatternResolver = ResourcePatternUtils .getResourcePatternResolver (resourceLoader );
147
228
}
148
229
149
230
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
+ }
157
239
}
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
+ }
161
247
}
162
- this .sessionFactory = config .buildSessionFactory ();
163
- }
164
248
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 ());
188
253
}
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 ());
191
260
}
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 ());
194
267
}
195
268
}
196
- }
197
269
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" );
207
277
}
278
+ sfb .addDirectory (file );
208
279
}
209
280
}
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 ();
211
297
}
212
298
213
299
0 commit comments