001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.orm.hibernate4; 018 019import java.io.File; 020import java.io.IOException; 021import java.util.Properties; 022import javax.sql.DataSource; 023 024import org.hibernate.Interceptor; 025import org.hibernate.SessionFactory; 026import org.hibernate.cache.spi.RegionFactory; 027import org.hibernate.cfg.Configuration; 028 029import org.springframework.beans.factory.DisposableBean; 030import org.springframework.beans.factory.FactoryBean; 031import org.springframework.beans.factory.InitializingBean; 032import org.springframework.context.ResourceLoaderAware; 033import org.springframework.core.io.ClassPathResource; 034import org.springframework.core.io.Resource; 035import org.springframework.core.io.ResourceLoader; 036import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 037import org.springframework.core.io.support.ResourcePatternResolver; 038import org.springframework.core.io.support.ResourcePatternUtils; 039import org.springframework.core.type.filter.TypeFilter; 040 041/** 042 * {@link org.springframework.beans.factory.FactoryBean} that creates a Hibernate 043 * {@link org.hibernate.SessionFactory}. This is the usual way to set up a shared 044 * Hibernate SessionFactory in a Spring application context; the SessionFactory can 045 * then be passed to Hibernate-based data access objects via dependency injection. 046 * 047 * <p><b>This variant of LocalSessionFactoryBean requires Hibernate 4.0 or higher.</b> 048 * As of Spring 4.0, it is compatible with (the quite refactored) Hibernate 4.3 as well. 049 * We recommend using the latest Hibernate 4.2.x or 4.3.x version, depending on whether 050 * you need to remain JPA 2.0 compatible at runtime (Hibernate 4.2) or can upgrade to 051 * JPA 2.1 (Hibernate 4.3). 052 * 053 * <p>This class is similar in role to the same-named class in the {@code orm.hibernate3} 054 * package. However, in practice, it is closer to {@code AnnotationSessionFactoryBean} 055 * since its core purpose is to bootstrap a {@code SessionFactory} from package scanning. 056 * 057 * <p><b>NOTE:</b> To set up Hibernate 4 for Spring-driven JTA transactions, make 058 * sure to either specify the {@link #setJtaTransactionManager "jtaTransactionManager"} 059 * bean property or to set the "hibernate.transaction.factory_class" property to 060 * {@link org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory}. 061 * Otherwise, Hibernate's smart flushing mechanism won't work properly. 062 * 063 * @author Juergen Hoeller 064 * @since 3.1 065 * @see #setDataSource 066 * @see #setPackagesToScan 067 * @see LocalSessionFactoryBuilder 068 */ 069public class LocalSessionFactoryBean extends HibernateExceptionTranslator 070 implements FactoryBean<SessionFactory>, ResourceLoaderAware, InitializingBean, DisposableBean { 071 072 private DataSource dataSource; 073 074 private Resource[] configLocations; 075 076 private String[] mappingResources; 077 078 private Resource[] mappingLocations; 079 080 private Resource[] cacheableMappingLocations; 081 082 private Resource[] mappingJarLocations; 083 084 private Resource[] mappingDirectoryLocations; 085 086 private Interceptor entityInterceptor; 087 088 @SuppressWarnings("deprecation") 089 private org.hibernate.cfg.NamingStrategy namingStrategy; 090 091 private Object jtaTransactionManager; 092 093 private Object multiTenantConnectionProvider; 094 095 private Object currentTenantIdentifierResolver; 096 097 private RegionFactory cacheRegionFactory; 098 099 private TypeFilter[] entityTypeFilters; 100 101 private Properties hibernateProperties; 102 103 private Class<?>[] annotatedClasses; 104 105 private String[] annotatedPackages; 106 107 private String[] packagesToScan; 108 109 private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); 110 111 private Configuration configuration; 112 113 private SessionFactory sessionFactory; 114 115 116 /** 117 * Set the DataSource to be used by the SessionFactory. 118 * If set, this will override corresponding settings in Hibernate properties. 119 * <p>If this is set, the Hibernate settings should not define 120 * a connection provider to avoid meaningless double configuration. 121 */ 122 public void setDataSource(DataSource dataSource) { 123 this.dataSource = dataSource; 124 } 125 126 /** 127 * Set the location of a single Hibernate XML config file, for example as 128 * classpath resource "classpath:hibernate.cfg.xml". 129 * <p>Note: Can be omitted when all necessary properties and mapping 130 * resources are specified locally via this bean. 131 * @see org.hibernate.cfg.Configuration#configure(java.net.URL) 132 */ 133 public void setConfigLocation(Resource configLocation) { 134 this.configLocations = new Resource[] {configLocation}; 135 } 136 137 /** 138 * Set the locations of multiple Hibernate XML config files, for example as 139 * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml". 140 * <p>Note: Can be omitted when all necessary properties and mapping 141 * resources are specified locally via this bean. 142 * @see org.hibernate.cfg.Configuration#configure(java.net.URL) 143 */ 144 public void setConfigLocations(Resource... configLocations) { 145 this.configLocations = configLocations; 146 } 147 148 /** 149 * Set Hibernate mapping resources to be found in the class path, 150 * like "example.hbm.xml" or "mypackage/example.hbm.xml". 151 * Analogous to mapping entries in a Hibernate XML config file. 152 * Alternative to the more generic setMappingLocations method. 153 * <p>Can be used to add to mappings from a Hibernate XML config file, 154 * or to specify all mappings locally. 155 * @see #setMappingLocations 156 * @see org.hibernate.cfg.Configuration#addResource 157 */ 158 public void setMappingResources(String... mappingResources) { 159 this.mappingResources = mappingResources; 160 } 161 162 /** 163 * Set locations of Hibernate mapping files, for example as classpath 164 * resource "classpath:example.hbm.xml". Supports any resource location 165 * via Spring's resource abstraction, for example relative paths like 166 * "WEB-INF/mappings/example.hbm.xml" when running in an application context. 167 * <p>Can be used to add to mappings from a Hibernate XML config file, 168 * or to specify all mappings locally. 169 * @see org.hibernate.cfg.Configuration#addInputStream 170 */ 171 public void setMappingLocations(Resource... mappingLocations) { 172 this.mappingLocations = mappingLocations; 173 } 174 175 /** 176 * Set locations of cacheable Hibernate mapping files, for example as web app 177 * resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location 178 * via Spring's resource abstraction, as long as the resource can be resolved 179 * in the file system. 180 * <p>Can be used to add to mappings from a Hibernate XML config file, 181 * or to specify all mappings locally. 182 * @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File) 183 */ 184 public void setCacheableMappingLocations(Resource... cacheableMappingLocations) { 185 this.cacheableMappingLocations = cacheableMappingLocations; 186 } 187 188 /** 189 * Set locations of jar files that contain Hibernate mapping resources, 190 * like "WEB-INF/lib/example.hbm.jar". 191 * <p>Can be used to add to mappings from a Hibernate XML config file, 192 * or to specify all mappings locally. 193 * @see org.hibernate.cfg.Configuration#addJar(java.io.File) 194 */ 195 public void setMappingJarLocations(Resource... mappingJarLocations) { 196 this.mappingJarLocations = mappingJarLocations; 197 } 198 199 /** 200 * Set locations of directories that contain Hibernate mapping resources, 201 * like "WEB-INF/mappings". 202 * <p>Can be used to add to mappings from a Hibernate XML config file, 203 * or to specify all mappings locally. 204 * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File) 205 */ 206 public void setMappingDirectoryLocations(Resource... mappingDirectoryLocations) { 207 this.mappingDirectoryLocations = mappingDirectoryLocations; 208 } 209 210 /** 211 * Set a Hibernate entity interceptor that allows to inspect and change 212 * property values before writing to and reading from the database. 213 * Will get applied to any new Session created by this factory. 214 * @see org.hibernate.cfg.Configuration#setInterceptor 215 */ 216 public void setEntityInterceptor(Interceptor entityInterceptor) { 217 this.entityInterceptor = entityInterceptor; 218 } 219 220 /** 221 * Set a Hibernate NamingStrategy for the SessionFactory, determining the 222 * physical column and table names given the info in the mapping document. 223 * @see org.hibernate.cfg.Configuration#setNamingStrategy 224 */ 225 @SuppressWarnings("deprecation") 226 public void setNamingStrategy(org.hibernate.cfg.NamingStrategy namingStrategy) { 227 this.namingStrategy = namingStrategy; 228 } 229 230 /** 231 * Set the Spring {@link org.springframework.transaction.jta.JtaTransactionManager} 232 * or the JTA {@link javax.transaction.TransactionManager} to be used with Hibernate, 233 * if any. Implicitly sets up {@code JtaPlatform} and {@code CMTTransactionStrategy}. 234 * @see LocalSessionFactoryBuilder#setJtaTransactionManager 235 */ 236 public void setJtaTransactionManager(Object jtaTransactionManager) { 237 this.jtaTransactionManager = jtaTransactionManager; 238 } 239 240 /** 241 * Set a Hibernate 4.1/4.2/4.3 {@code MultiTenantConnectionProvider} to be passed 242 * on to the SessionFactory: as an instance, a Class, or a String class name. 243 * <p>Note that the package location of the {@code MultiTenantConnectionProvider} 244 * interface changed between Hibernate 4.2 and 4.3. This method accepts both variants. 245 * @since 4.0 246 * @see LocalSessionFactoryBuilder#setMultiTenantConnectionProvider 247 */ 248 public void setMultiTenantConnectionProvider(Object multiTenantConnectionProvider) { 249 this.multiTenantConnectionProvider = multiTenantConnectionProvider; 250 } 251 252 /** 253 * Set a Hibernate 4.1/4.2/4.3 {@code CurrentTenantIdentifierResolver} to be passed 254 * on to the SessionFactory: as an instance, a Class, or a String class name. 255 * @since 4.0 256 * @see LocalSessionFactoryBuilder#setCurrentTenantIdentifierResolver 257 */ 258 public void setCurrentTenantIdentifierResolver(Object currentTenantIdentifierResolver) { 259 this.currentTenantIdentifierResolver = currentTenantIdentifierResolver; 260 } 261 262 /** 263 * Set the Hibernate RegionFactory to use for the SessionFactory. 264 * Allows for using a Spring-managed RegionFactory instance. 265 * <p>Note: If this is set, the Hibernate settings should not define a 266 * cache provider to avoid meaningless double configuration. 267 * @since 4.0 268 * @see org.hibernate.cache.spi.RegionFactory 269 * @see LocalSessionFactoryBuilder#setCacheRegionFactory 270 */ 271 public void setCacheRegionFactory(RegionFactory cacheRegionFactory) { 272 this.cacheRegionFactory = cacheRegionFactory; 273 } 274 275 /** 276 * Specify custom type filters for Spring-based scanning for entity classes. 277 * <p>Default is to search all specified packages for classes annotated with 278 * {@code @javax.persistence.Entity}, {@code @javax.persistence.Embeddable} 279 * or {@code @javax.persistence.MappedSuperclass}. 280 * @since 4.1 281 * @see #setPackagesToScan 282 */ 283 public void setEntityTypeFilters(TypeFilter... entityTypeFilters) { 284 this.entityTypeFilters = entityTypeFilters; 285 } 286 287 /** 288 * Set Hibernate properties, such as "hibernate.dialect". 289 * <p>Note: Do not specify a transaction provider here when using 290 * Spring-driven transactions. It is also advisable to omit connection 291 * provider settings and use a Spring-set DataSource instead. 292 * @see #setDataSource 293 */ 294 public void setHibernateProperties(Properties hibernateProperties) { 295 this.hibernateProperties = hibernateProperties; 296 } 297 298 /** 299 * Return the Hibernate properties, if any. Mainly available for 300 * configuration through property paths that specify individual keys. 301 */ 302 public Properties getHibernateProperties() { 303 if (this.hibernateProperties == null) { 304 this.hibernateProperties = new Properties(); 305 } 306 return this.hibernateProperties; 307 } 308 309 /** 310 * Specify annotated entity classes to register with this Hibernate SessionFactory. 311 * @see org.hibernate.cfg.Configuration#addAnnotatedClass(Class) 312 */ 313 public void setAnnotatedClasses(Class<?>... annotatedClasses) { 314 this.annotatedClasses = annotatedClasses; 315 } 316 317 /** 318 * Specify the names of annotated packages, for which package-level 319 * annotation metadata will be read. 320 * @see org.hibernate.cfg.Configuration#addPackage(String) 321 */ 322 public void setAnnotatedPackages(String... annotatedPackages) { 323 this.annotatedPackages = annotatedPackages; 324 } 325 326 /** 327 * Specify packages to search for autodetection of your entity classes in the 328 * classpath. This is analogous to Spring's component-scan feature 329 * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}). 330 */ 331 public void setPackagesToScan(String... packagesToScan) { 332 this.packagesToScan = packagesToScan; 333 } 334 335 @Override 336 public void setResourceLoader(ResourceLoader resourceLoader) { 337 this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); 338 } 339 340 341 @Override 342 public void afterPropertiesSet() throws IOException { 343 LocalSessionFactoryBuilder sfb = new LocalSessionFactoryBuilder(this.dataSource, this.resourcePatternResolver); 344 345 if (this.configLocations != null) { 346 for (Resource resource : this.configLocations) { 347 // Load Hibernate configuration from given location. 348 sfb.configure(resource.getURL()); 349 } 350 } 351 352 if (this.mappingResources != null) { 353 // Register given Hibernate mapping definitions, contained in resource files. 354 for (String mapping : this.mappingResources) { 355 Resource mr = new ClassPathResource(mapping.trim(), this.resourcePatternResolver.getClassLoader()); 356 sfb.addInputStream(mr.getInputStream()); 357 } 358 } 359 360 if (this.mappingLocations != null) { 361 // Register given Hibernate mapping definitions, contained in resource files. 362 for (Resource resource : this.mappingLocations) { 363 sfb.addInputStream(resource.getInputStream()); 364 } 365 } 366 367 if (this.cacheableMappingLocations != null) { 368 // Register given cacheable Hibernate mapping definitions, read from the file system. 369 for (Resource resource : this.cacheableMappingLocations) { 370 sfb.addCacheableFile(resource.getFile()); 371 } 372 } 373 374 if (this.mappingJarLocations != null) { 375 // Register given Hibernate mapping definitions, contained in jar files. 376 for (Resource resource : this.mappingJarLocations) { 377 sfb.addJar(resource.getFile()); 378 } 379 } 380 381 if (this.mappingDirectoryLocations != null) { 382 // Register all Hibernate mapping definitions in the given directories. 383 for (Resource resource : this.mappingDirectoryLocations) { 384 File file = resource.getFile(); 385 if (!file.isDirectory()) { 386 throw new IllegalArgumentException( 387 "Mapping directory location [" + resource + "] does not denote a directory"); 388 } 389 sfb.addDirectory(file); 390 } 391 } 392 393 if (this.entityInterceptor != null) { 394 sfb.setInterceptor(this.entityInterceptor); 395 } 396 397 if (this.namingStrategy != null) { 398 sfb.setNamingStrategy(this.namingStrategy); 399 } 400 401 if (this.jtaTransactionManager != null) { 402 sfb.setJtaTransactionManager(this.jtaTransactionManager); 403 } 404 405 if (this.multiTenantConnectionProvider != null) { 406 sfb.setMultiTenantConnectionProvider(this.multiTenantConnectionProvider); 407 } 408 409 if (this.currentTenantIdentifierResolver != null) { 410 sfb.setCurrentTenantIdentifierResolver(this.currentTenantIdentifierResolver); 411 } 412 413 if (this.cacheRegionFactory != null) { 414 sfb.setCacheRegionFactory(this.cacheRegionFactory); 415 } 416 417 if (this.entityTypeFilters != null) { 418 sfb.setEntityTypeFilters(this.entityTypeFilters); 419 } 420 421 if (this.hibernateProperties != null) { 422 sfb.addProperties(this.hibernateProperties); 423 } 424 425 if (this.annotatedClasses != null) { 426 sfb.addAnnotatedClasses(this.annotatedClasses); 427 } 428 429 if (this.annotatedPackages != null) { 430 sfb.addPackages(this.annotatedPackages); 431 } 432 433 if (this.packagesToScan != null) { 434 sfb.scanPackages(this.packagesToScan); 435 } 436 437 // Build SessionFactory instance. 438 this.configuration = sfb; 439 this.sessionFactory = buildSessionFactory(sfb); 440 } 441 442 /** 443 * Subclasses can override this method to perform custom initialization 444 * of the SessionFactory instance, creating it via the given Configuration 445 * object that got prepared by this LocalSessionFactoryBean. 446 * <p>The default implementation invokes LocalSessionFactoryBuilder's buildSessionFactory. 447 * A custom implementation could prepare the instance in a specific way (e.g. applying 448 * a custom ServiceRegistry) or use a custom SessionFactoryImpl subclass. 449 * @param sfb LocalSessionFactoryBuilder prepared by this LocalSessionFactoryBean 450 * @return the SessionFactory instance 451 * @see LocalSessionFactoryBuilder#buildSessionFactory 452 */ 453 protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) { 454 return sfb.buildSessionFactory(); 455 } 456 457 /** 458 * Return the Hibernate Configuration object used to build the SessionFactory. 459 * Allows for access to configuration metadata stored there (rarely needed). 460 * @throws IllegalStateException if the Configuration object has not been initialized yet 461 */ 462 public final Configuration getConfiguration() { 463 if (this.configuration == null) { 464 throw new IllegalStateException("Configuration not initialized yet"); 465 } 466 return this.configuration; 467 } 468 469 470 @Override 471 public SessionFactory getObject() { 472 return this.sessionFactory; 473 } 474 475 @Override 476 public Class<?> getObjectType() { 477 return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class); 478 } 479 480 @Override 481 public boolean isSingleton() { 482 return true; 483 } 484 485 486 @Override 487 public void destroy() { 488 this.sessionFactory.close(); 489 } 490 491}