1 /* 2 * Copyright 2002-2012 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.orm.hibernate4; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.util.Properties; 22 import javax.sql.DataSource; 23 24 import org.hibernate.Interceptor; 25 import org.hibernate.SessionFactory; 26 import org.hibernate.cfg.Configuration; 27 import org.hibernate.cfg.NamingStrategy; 28 29 import org.springframework.beans.factory.DisposableBean; 30 import org.springframework.beans.factory.FactoryBean; 31 import org.springframework.beans.factory.InitializingBean; 32 import org.springframework.context.ResourceLoaderAware; 33 import org.springframework.core.io.ClassPathResource; 34 import org.springframework.core.io.Resource; 35 import org.springframework.core.io.ResourceLoader; 36 import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 37 import org.springframework.core.io.support.ResourcePatternResolver; 38 import org.springframework.core.io.support.ResourcePatternUtils; 39 40 /** 41 * {@link org.springframework.beans.factory.FactoryBean} that creates a Hibernate 42 * {@link org.hibernate.SessionFactory}. This is the usual way to set up a shared 43 * Hibernate SessionFactory in a Spring application context; the SessionFactory can 44 * then be passed to Hibernate-based data access objects via dependency injection. 45 * 46 * <p><b>NOTE:</b> This variant of LocalSessionFactoryBean requires Hibernate 4.0 or higher. 47 * It is similar in role to the same-named class in the <code>orm.hibernate3</code> package. 48 * However, in practice, it is closer to <code>AnnotationSessionFactoryBean</code> since 49 * its core purpose is to bootstrap a <code>SessionFactory</code> from annotation scanning. 50 * 51 * <p><b>NOTE:</b> To set up Hibernate 4 for Spring-driven JTA transactions, make 52 * sure to either specify the {@link #setJtaTransactionManager "jtaTransactionManager"} 53 * bean property or to set the "hibernate.transaction.factory_class" property to 54 * {@link org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory}. 55 * Otherwise, Hibernate's smart flushing mechanism won't work properly. 56 * 57 * @author Juergen Hoeller 58 * @since 3.1 59 * @see #setDataSource 60 * @see #setPackagesToScan 61 */ 62 public class LocalSessionFactoryBean extends HibernateExceptionTranslator 63 implements FactoryBean<SessionFactory>, ResourceLoaderAware, InitializingBean, DisposableBean { 64 65 private DataSource dataSource; 66 67 private Resource[] configLocations; 68 69 private String[] mappingResources; 70 71 private Resource[] mappingLocations; 72 73 private Resource[] cacheableMappingLocations; 74 75 private Resource[] mappingJarLocations; 76 77 private Resource[] mappingDirectoryLocations; 78 79 private Interceptor entityInterceptor; 80 81 private NamingStrategy namingStrategy; 82 83 private Properties hibernateProperties; 84 85 private Class<?>[] annotatedClasses; 86 87 private String[] annotatedPackages; 88 89 private String[] packagesToScan; 90 91 private Object jtaTransactionManager; 92 93 private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); 94 95 private Configuration configuration; 96 97 private SessionFactory sessionFactory; 98 99 100 /** 101 * Set the DataSource to be used by the SessionFactory. 102 * If set, this will override corresponding settings in Hibernate properties. 103 * <p>If this is set, the Hibernate settings should not define 104 * a connection provider to avoid meaningless double configuration. 105 */ setDataSource(DataSource dataSource)106 public void setDataSource(DataSource dataSource) { 107 this.dataSource = dataSource; 108 } 109 110 /** 111 * Set the location of a single Hibernate XML config file, for example as 112 * classpath resource "classpath:hibernate.cfg.xml". 113 * <p>Note: Can be omitted when all necessary properties and mapping 114 * resources are specified locally via this bean. 115 * @see org.hibernate.cfg.Configuration#configure(java.net.URL) 116 */ setConfigLocation(Resource configLocation)117 public void setConfigLocation(Resource configLocation) { 118 this.configLocations = new Resource[] {configLocation}; 119 } 120 121 /** 122 * Set the locations of multiple Hibernate XML config files, for example as 123 * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml". 124 * <p>Note: Can be omitted when all necessary properties and mapping 125 * resources are specified locally via this bean. 126 * @see org.hibernate.cfg.Configuration#configure(java.net.URL) 127 */ setConfigLocations(Resource[] configLocations)128 public void setConfigLocations(Resource[] configLocations) { 129 this.configLocations = configLocations; 130 } 131 132 /** 133 * Set Hibernate mapping resources to be found in the class path, 134 * like "example.hbm.xml" or "mypackage/example.hbm.xml". 135 * Analogous to mapping entries in a Hibernate XML config file. 136 * Alternative to the more generic setMappingLocations method. 137 * <p>Can be used to add to mappings from a Hibernate XML config file, 138 * or to specify all mappings locally. 139 * @see #setMappingLocations 140 * @see org.hibernate.cfg.Configuration#addResource 141 */ setMappingResources(String[] mappingResources)142 public void setMappingResources(String[] mappingResources) { 143 this.mappingResources = mappingResources; 144 } 145 146 /** 147 * Set locations of Hibernate mapping files, for example as classpath 148 * resource "classpath:example.hbm.xml". Supports any resource location 149 * via Spring's resource abstraction, for example relative paths like 150 * "WEB-INF/mappings/example.hbm.xml" when running in an application context. 151 * <p>Can be used to add to mappings from a Hibernate XML config file, 152 * or to specify all mappings locally. 153 * @see org.hibernate.cfg.Configuration#addInputStream 154 */ setMappingLocations(Resource[] mappingLocations)155 public void setMappingLocations(Resource[] mappingLocations) { 156 this.mappingLocations = mappingLocations; 157 } 158 159 /** 160 * Set locations of cacheable Hibernate mapping files, for example as web app 161 * resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location 162 * via Spring's resource abstraction, as long as the resource can be resolved 163 * in the file system. 164 * <p>Can be used to add to mappings from a Hibernate XML config file, 165 * or to specify all mappings locally. 166 * @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File) 167 */ setCacheableMappingLocations(Resource[] cacheableMappingLocations)168 public void setCacheableMappingLocations(Resource[] cacheableMappingLocations) { 169 this.cacheableMappingLocations = cacheableMappingLocations; 170 } 171 172 /** 173 * Set locations of jar files that contain Hibernate mapping resources, 174 * like "WEB-INF/lib/example.hbm.jar". 175 * <p>Can be used to add to mappings from a Hibernate XML config file, 176 * or to specify all mappings locally. 177 * @see org.hibernate.cfg.Configuration#addJar(java.io.File) 178 */ setMappingJarLocations(Resource[] mappingJarLocations)179 public void setMappingJarLocations(Resource[] mappingJarLocations) { 180 this.mappingJarLocations = mappingJarLocations; 181 } 182 183 /** 184 * Set locations of directories that contain Hibernate mapping resources, 185 * like "WEB-INF/mappings". 186 * <p>Can be used to add to mappings from a Hibernate XML config file, 187 * or to specify all mappings locally. 188 * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File) 189 */ setMappingDirectoryLocations(Resource[] mappingDirectoryLocations)190 public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) { 191 this.mappingDirectoryLocations = mappingDirectoryLocations; 192 } 193 194 /** 195 * Set a Hibernate entity interceptor that allows to inspect and change 196 * property values before writing to and reading from the database. 197 * Will get applied to any new Session created by this factory. 198 * @see org.hibernate.cfg.Configuration#setInterceptor 199 */ setEntityInterceptor(Interceptor entityInterceptor)200 public void setEntityInterceptor(Interceptor entityInterceptor) { 201 this.entityInterceptor = entityInterceptor; 202 } 203 204 /** 205 * Set a Hibernate NamingStrategy for the SessionFactory, determining the 206 * physical column and table names given the info in the mapping document. 207 * @see org.hibernate.cfg.Configuration#setNamingStrategy 208 */ setNamingStrategy(NamingStrategy namingStrategy)209 public void setNamingStrategy(NamingStrategy namingStrategy) { 210 this.namingStrategy = namingStrategy; 211 } 212 213 /** 214 * Set Hibernate properties, such as "hibernate.dialect". 215 * <p>Note: Do not specify a transaction provider here when using 216 * Spring-driven transactions. It is also advisable to omit connection 217 * provider settings and use a Spring-set DataSource instead. 218 * @see #setDataSource 219 */ setHibernateProperties(Properties hibernateProperties)220 public void setHibernateProperties(Properties hibernateProperties) { 221 this.hibernateProperties = hibernateProperties; 222 } 223 224 /** 225 * Return the Hibernate properties, if any. Mainly available for 226 * configuration through property paths that specify individual keys. 227 */ getHibernateProperties()228 public Properties getHibernateProperties() { 229 if (this.hibernateProperties == null) { 230 this.hibernateProperties = new Properties(); 231 } 232 return this.hibernateProperties; 233 } 234 235 /** 236 * Specify annotated entity classes to register with this Hibernate SessionFactory. 237 * @see org.hibernate.cfg.Configuration#addAnnotatedClass(Class) 238 */ setAnnotatedClasses(Class<?>[] annotatedClasses)239 public void setAnnotatedClasses(Class<?>[] annotatedClasses) { 240 this.annotatedClasses = annotatedClasses; 241 } 242 243 /** 244 * Specify the names of annotated packages, for which package-level 245 * annotation metadata will be read. 246 * @see org.hibernate.cfg.Configuration#addPackage(String) 247 */ setAnnotatedPackages(String[] annotatedPackages)248 public void setAnnotatedPackages(String[] annotatedPackages) { 249 this.annotatedPackages = annotatedPackages; 250 } 251 252 /** 253 * Specify packages to search for autodetection of your entity classes in the 254 * classpath. This is analogous to Spring's component-scan feature 255 * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}). 256 */ setPackagesToScan(String... packagesToScan)257 public void setPackagesToScan(String... packagesToScan) { 258 this.packagesToScan = packagesToScan; 259 } 260 261 /** 262 * Set the Spring {@link org.springframework.transaction.jta.JtaTransactionManager} 263 * or the JTA {@link javax.transaction.TransactionManager} to be used with Hibernate, 264 * if any. 265 * @see LocalSessionFactoryBuilder#setJtaTransactionManager 266 */ setJtaTransactionManager(Object jtaTransactionManager)267 public void setJtaTransactionManager(Object jtaTransactionManager) { 268 this.jtaTransactionManager = jtaTransactionManager; 269 } 270 setResourceLoader(ResourceLoader resourceLoader)271 public void setResourceLoader(ResourceLoader resourceLoader) { 272 this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); 273 } 274 275 afterPropertiesSet()276 public void afterPropertiesSet() throws IOException { 277 LocalSessionFactoryBuilder sfb = new LocalSessionFactoryBuilder(this.dataSource, this.resourcePatternResolver); 278 279 if (this.configLocations != null) { 280 for (Resource resource : this.configLocations) { 281 // Load Hibernate configuration from given location. 282 sfb.configure(resource.getURL()); 283 } 284 } 285 286 if (this.mappingResources != null) { 287 // Register given Hibernate mapping definitions, contained in resource files. 288 for (String mapping : this.mappingResources) { 289 Resource mr = new ClassPathResource(mapping.trim(), this.resourcePatternResolver.getClassLoader()); 290 sfb.addInputStream(mr.getInputStream()); 291 } 292 } 293 294 if (this.mappingLocations != null) { 295 // Register given Hibernate mapping definitions, contained in resource files. 296 for (Resource resource : this.mappingLocations) { 297 sfb.addInputStream(resource.getInputStream()); 298 } 299 } 300 301 if (this.cacheableMappingLocations != null) { 302 // Register given cacheable Hibernate mapping definitions, read from the file system. 303 for (Resource resource : this.cacheableMappingLocations) { 304 sfb.addCacheableFile(resource.getFile()); 305 } 306 } 307 308 if (this.mappingJarLocations != null) { 309 // Register given Hibernate mapping definitions, contained in jar files. 310 for (Resource resource : this.mappingJarLocations) { 311 sfb.addJar(resource.getFile()); 312 } 313 } 314 315 if (this.mappingDirectoryLocations != null) { 316 // Register all Hibernate mapping definitions in the given directories. 317 for (Resource resource : this.mappingDirectoryLocations) { 318 File file = resource.getFile(); 319 if (!file.isDirectory()) { 320 throw new IllegalArgumentException( 321 "Mapping directory location [" + resource + "] does not denote a directory"); 322 } 323 sfb.addDirectory(file); 324 } 325 } 326 327 if (this.entityInterceptor != null) { 328 sfb.setInterceptor(this.entityInterceptor); 329 } 330 331 if (this.namingStrategy != null) { 332 sfb.setNamingStrategy(this.namingStrategy); 333 } 334 335 if (this.hibernateProperties != null) { 336 sfb.addProperties(this.hibernateProperties); 337 } 338 339 if (this.annotatedClasses != null) { 340 sfb.addAnnotatedClasses(this.annotatedClasses); 341 } 342 343 if (this.annotatedPackages != null) { 344 sfb.addPackages(this.annotatedPackages); 345 } 346 347 if (this.packagesToScan != null) { 348 sfb.scanPackages(this.packagesToScan); 349 } 350 351 if (this.jtaTransactionManager != null) { 352 sfb.setJtaTransactionManager(this.jtaTransactionManager); 353 } 354 355 // Build SessionFactory instance. 356 this.configuration = sfb; 357 this.sessionFactory = buildSessionFactory(sfb); 358 } 359 360 /** 361 * Subclasses can override this method to perform custom initialization 362 * of the SessionFactory instance, creating it via the given Configuration 363 * object that got prepared by this LocalSessionFactoryBean. 364 * <p>The default implementation invokes LocalSessionFactoryBuilder's buildSessionFactory. 365 * A custom implementation could prepare the instance in a specific way (e.g. applying 366 * a custom ServiceRegistry) or use a custom SessionFactoryImpl subclass. 367 * @param sfb LocalSessionFactoryBuilder prepared by this LocalSessionFactoryBean 368 * @return the SessionFactory instance 369 * @see LocalSessionFactoryBuilder#buildSessionFactory 370 */ buildSessionFactory(LocalSessionFactoryBuilder sfb)371 protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) { 372 return sfb.buildSessionFactory(); 373 } 374 375 /** 376 * Return the Hibernate Configuration object used to build the SessionFactory. 377 * Allows for access to configuration metadata stored there (rarely needed). 378 * @throws IllegalStateException if the Configuration object has not been initialized yet 379 */ getConfiguration()380 public final Configuration getConfiguration() { 381 if (this.configuration == null) { 382 throw new IllegalStateException("Configuration not initialized yet"); 383 } 384 return this.configuration; 385 } 386 387 getObject()388 public SessionFactory getObject() { 389 return this.sessionFactory; 390 } 391 getObjectType()392 public Class<?> getObjectType() { 393 return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class); 394 } 395 isSingleton()396 public boolean isSingleton() { 397 return true; 398 } 399 400 destroy()401 public void destroy() { 402 this.sessionFactory.close(); 403 } 404 405 } 406