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.hibernate3; 018 019import java.io.File; 020import java.lang.reflect.Array; 021import java.sql.Connection; 022import java.sql.SQLException; 023import java.sql.Statement; 024import java.util.Collection; 025import java.util.Enumeration; 026import java.util.Map; 027import java.util.Properties; 028import javax.sql.DataSource; 029import javax.transaction.TransactionManager; 030 031import org.hibernate.HibernateException; 032import org.hibernate.Interceptor; 033import org.hibernate.Session; 034import org.hibernate.SessionFactory; 035import org.hibernate.cache.RegionFactory; 036import org.hibernate.cfg.Configuration; 037import org.hibernate.cfg.Environment; 038import org.hibernate.cfg.Mappings; 039import org.hibernate.cfg.NamingStrategy; 040import org.hibernate.dialect.Dialect; 041import org.hibernate.engine.FilterDefinition; 042import org.hibernate.engine.SessionFactoryImplementor; 043import org.hibernate.event.EventListeners; 044import org.hibernate.tool.hbm2ddl.DatabaseMetadata; 045import org.hibernate.transaction.JTATransactionFactory; 046 047import org.springframework.beans.BeanUtils; 048import org.springframework.beans.factory.BeanClassLoaderAware; 049import org.springframework.core.io.ClassPathResource; 050import org.springframework.core.io.Resource; 051import org.springframework.dao.DataAccessException; 052import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; 053import org.springframework.jdbc.support.JdbcUtils; 054import org.springframework.jdbc.support.lob.LobHandler; 055import org.springframework.util.ClassUtils; 056import org.springframework.util.StringUtils; 057 058/** 059 * {@link org.springframework.beans.factory.FactoryBean} that creates a 060 * Hibernate {@link org.hibernate.SessionFactory}. This is the usual way to 061 * set up a shared Hibernate SessionFactory in a Spring application context; 062 * the SessionFactory can then be passed to Hibernate-based DAOs via 063 * dependency injection. 064 * 065 * <p>Configuration settings can either be read from a Hibernate XML file, 066 * specified as "configLocation", or completely via this class. A typical 067 * local configuration consists of one or more "mappingResources", various 068 * "hibernateProperties" (not strictly necessary), and a "dataSource" that the 069 * SessionFactory should use. The latter can also be specified via Hibernate 070 * properties, but "dataSource" supports any Spring-configured DataSource, 071 * instead of relying on Hibernate's own connection providers. 072 * 073 * <p>This SessionFactory handling strategy is appropriate for most types of 074 * applications, from Hibernate-only single database apps to ones that need 075 * distributed transactions. Either {@link HibernateTransactionManager} or 076 * {@link org.springframework.transaction.jta.JtaTransactionManager} can be 077 * used for transaction demarcation, with the latter only necessary for 078 * transactions which span multiple databases. 079 * 080 * <p>This factory bean will by default expose a transaction-aware SessionFactory 081 * proxy, letting data access code work with the plain Hibernate SessionFactory 082 * and its {@code getCurrentSession()} method, while still being able to 083 * participate in current Spring-managed transactions: with any transaction 084 * management strategy, either local or JTA / EJB CMT, and any transaction 085 * synchronization mechanism, either Spring or JTA. Furthermore, 086 * {@code getCurrentSession()} will also seamlessly work with 087 * a request-scoped Session managed by 088 * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter} / 089 * {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}. 090 * 091 * <p>Requires Hibernate 3.6.x, as of Spring 4.0. 092 * Note that this factory will use "on_close" as default Hibernate connection 093 * release mode, unless in the case of a "jtaTransactionManager" specified, 094 * for the reason that this is appropriate for most Spring-based applications 095 * (in particular when using Spring's HibernateTransactionManager). 096 * 097 * @author Juergen Hoeller 098 * @since 1.2 099 * @see HibernateTemplate#setSessionFactory 100 * @see HibernateTransactionManager#setSessionFactory 101 * @see #setExposeTransactionAwareSessionFactory 102 * @see #setJtaTransactionManager 103 * @see org.hibernate.SessionFactory#getCurrentSession() 104 * @see HibernateTransactionManager 105 * @deprecated as of Spring 4.3, in favor of Hibernate 4.x/5.x 106 */ 107@Deprecated 108public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware { 109 110 private static final ThreadLocal<DataSource> configTimeDataSourceHolder = 111 new ThreadLocal<DataSource>(); 112 113 private static final ThreadLocal<TransactionManager> configTimeTransactionManagerHolder = 114 new ThreadLocal<TransactionManager>(); 115 116 private static final ThreadLocal<Object> configTimeRegionFactoryHolder = 117 new ThreadLocal<Object>(); 118 119 private static final ThreadLocal<LobHandler> configTimeLobHandlerHolder = 120 new ThreadLocal<LobHandler>(); 121 122 /** 123 * Return the DataSource for the currently configured Hibernate SessionFactory, 124 * to be used by LocalDataSourceConnectionProvoder. 125 * <p>This instance will be set before initialization of the corresponding 126 * SessionFactory, and reset immediately afterwards. It is thus only available 127 * during configuration. 128 * @see #setDataSource 129 * @see LocalDataSourceConnectionProvider 130 */ 131 public static DataSource getConfigTimeDataSource() { 132 return configTimeDataSourceHolder.get(); 133 } 134 135 /** 136 * Return the JTA TransactionManager for the currently configured Hibernate 137 * SessionFactory, to be used by LocalTransactionManagerLookup. 138 * <p>This instance will be set before initialization of the corresponding 139 * SessionFactory, and reset immediately afterwards. It is thus only available 140 * during configuration. 141 * @see #setJtaTransactionManager 142 * @see LocalTransactionManagerLookup 143 */ 144 public static TransactionManager getConfigTimeTransactionManager() { 145 return configTimeTransactionManagerHolder.get(); 146 } 147 148 /** 149 * Return the RegionFactory for the currently configured Hibernate SessionFactory, 150 * to be used by LocalRegionFactoryProxy. 151 * <p>This instance will be set before initialization of the corresponding 152 * SessionFactory, and reset immediately afterwards. It is thus only available 153 * during configuration. 154 * @see #setCacheRegionFactory 155 */ 156 static Object getConfigTimeRegionFactory() { 157 return configTimeRegionFactoryHolder.get(); 158 } 159 160 /** 161 * Return the LobHandler for the currently configured Hibernate SessionFactory, 162 * to be used by UserType implementations like ClobStringType. 163 * <p>This instance will be set before initialization of the corresponding 164 * SessionFactory, and reset immediately afterwards. It is thus only available 165 * during configuration. 166 * @see #setLobHandler 167 * @see org.springframework.orm.hibernate3.support.ClobStringType 168 * @see org.springframework.orm.hibernate3.support.BlobByteArrayType 169 * @see org.springframework.orm.hibernate3.support.BlobSerializableType 170 */ 171 public static LobHandler getConfigTimeLobHandler() { 172 return configTimeLobHandlerHolder.get(); 173 } 174 175 176 private Class<? extends Configuration> configurationClass = Configuration.class; 177 178 private Resource[] configLocations; 179 180 private String[] mappingResources; 181 182 private Resource[] mappingLocations; 183 184 private Resource[] cacheableMappingLocations; 185 186 private Resource[] mappingJarLocations; 187 188 private Resource[] mappingDirectoryLocations; 189 190 private Properties hibernateProperties; 191 192 private TransactionManager jtaTransactionManager; 193 194 private RegionFactory cacheRegionFactory; 195 196 private LobHandler lobHandler; 197 198 private Interceptor entityInterceptor; 199 200 private NamingStrategy namingStrategy; 201 202 private TypeDefinitionBean[] typeDefinitions; 203 204 private FilterDefinition[] filterDefinitions; 205 206 private Properties entityCacheStrategies; 207 208 private Properties collectionCacheStrategies; 209 210 private Map<String, Object> eventListeners; 211 212 private boolean schemaUpdate = false; 213 214 private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); 215 216 private Configuration configuration; 217 218 219 /** 220 * Specify the Hibernate Configuration class to use. 221 * <p>Default is {@link org.hibernate.cfg.Configuration}; any subclass 222 * of this default Hibernate Configuration class can be specified. 223 */ 224 @SuppressWarnings("unchecked") 225 public void setConfigurationClass(Class<?> configurationClass) { 226 if (configurationClass == null || !Configuration.class.isAssignableFrom(configurationClass)) { 227 throw new IllegalArgumentException( 228 "'configurationClass' must be assignable to [org.hibernate.cfg.Configuration]"); 229 } 230 this.configurationClass = (Class<? extends Configuration>) configurationClass; 231 } 232 233 /** 234 * Set the location of a single Hibernate XML config file, for example as 235 * classpath resource "classpath:hibernate.cfg.xml". 236 * <p>Note: Can be omitted when all necessary properties and mapping 237 * resources are specified locally via this bean. 238 * @see org.hibernate.cfg.Configuration#configure(java.net.URL) 239 */ 240 public void setConfigLocation(Resource configLocation) { 241 this.configLocations = new Resource[] {configLocation}; 242 } 243 244 /** 245 * Set the locations of multiple Hibernate XML config files, for example as 246 * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml". 247 * <p>Note: Can be omitted when all necessary properties and mapping 248 * resources are specified locally via this bean. 249 * @see org.hibernate.cfg.Configuration#configure(java.net.URL) 250 */ 251 public void setConfigLocations(Resource... configLocations) { 252 this.configLocations = configLocations; 253 } 254 255 /** 256 * Set Hibernate mapping resources to be found in the class path, 257 * like "example.hbm.xml" or "mypackage/example.hbm.xml". 258 * Analogous to mapping entries in a Hibernate XML config file. 259 * Alternative to the more generic setMappingLocations method. 260 * <p>Can be used to add to mappings from a Hibernate XML config file, 261 * or to specify all mappings locally. 262 * @see #setMappingLocations 263 * @see org.hibernate.cfg.Configuration#addResource 264 */ 265 public void setMappingResources(String... mappingResources) { 266 this.mappingResources = mappingResources; 267 } 268 269 /** 270 * Set locations of Hibernate mapping files, for example as classpath 271 * resource "classpath:example.hbm.xml". Supports any resource location 272 * via Spring's resource abstraction, for example relative paths like 273 * "WEB-INF/mappings/example.hbm.xml" when running in an application context. 274 * <p>Can be used to add to mappings from a Hibernate XML config file, 275 * or to specify all mappings locally. 276 * @see org.hibernate.cfg.Configuration#addInputStream 277 */ 278 public void setMappingLocations(Resource... mappingLocations) { 279 this.mappingLocations = mappingLocations; 280 } 281 282 /** 283 * Set locations of cacheable Hibernate mapping files, for example as web app 284 * resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location 285 * via Spring's resource abstraction, as long as the resource can be resolved 286 * in the file system. 287 * <p>Can be used to add to mappings from a Hibernate XML config file, 288 * or to specify all mappings locally. 289 * @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File) 290 */ 291 public void setCacheableMappingLocations(Resource... cacheableMappingLocations) { 292 this.cacheableMappingLocations = cacheableMappingLocations; 293 } 294 295 /** 296 * Set locations of jar files that contain Hibernate mapping resources, 297 * like "WEB-INF/lib/example.hbm.jar". 298 * <p>Can be used to add to mappings from a Hibernate XML config file, 299 * or to specify all mappings locally. 300 * @see org.hibernate.cfg.Configuration#addJar(java.io.File) 301 */ 302 public void setMappingJarLocations(Resource... mappingJarLocations) { 303 this.mappingJarLocations = mappingJarLocations; 304 } 305 306 /** 307 * Set locations of directories that contain Hibernate mapping resources, 308 * like "WEB-INF/mappings". 309 * <p>Can be used to add to mappings from a Hibernate XML config file, 310 * or to specify all mappings locally. 311 * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File) 312 */ 313 public void setMappingDirectoryLocations(Resource... mappingDirectoryLocations) { 314 this.mappingDirectoryLocations = mappingDirectoryLocations; 315 } 316 317 /** 318 * Set Hibernate properties, such as "hibernate.dialect". 319 * <p>Can be used to override values in a Hibernate XML config file, 320 * or to specify all necessary properties locally. 321 * <p>Note: Do not specify a transaction provider here when using 322 * Spring-driven transactions. It is also advisable to omit connection 323 * provider settings and use a Spring-set DataSource instead. 324 * @see #setDataSource 325 */ 326 public void setHibernateProperties(Properties hibernateProperties) { 327 this.hibernateProperties = hibernateProperties; 328 } 329 330 /** 331 * Return the Hibernate properties, if any. Mainly available for 332 * configuration through property paths that specify individual keys. 333 */ 334 public Properties getHibernateProperties() { 335 if (this.hibernateProperties == null) { 336 this.hibernateProperties = new Properties(); 337 } 338 return this.hibernateProperties; 339 } 340 341 /** 342 * Set the JTA TransactionManager to be used for Hibernate's 343 * TransactionManagerLookup. Allows for using a Spring-managed 344 * JTA TransactionManager for Hibernate's cache synchronization. 345 * <p>Note: If this is set, the Hibernate settings should not define a 346 * transaction manager lookup to avoid meaningless double configuration. 347 * @see LocalTransactionManagerLookup 348 */ 349 public void setJtaTransactionManager(TransactionManager jtaTransactionManager) { 350 this.jtaTransactionManager = jtaTransactionManager; 351 } 352 353 /** 354 * Set the Hibernate RegionFactory to use for the SessionFactory. 355 * Allows for using a Spring-managed RegionFactory instance. 356 * <p>Note: If this is set, the Hibernate settings should not define a 357 * cache provider to avoid meaningless double configuration. 358 * @see org.hibernate.cache.RegionFactory 359 */ 360 public void setCacheRegionFactory(RegionFactory cacheRegionFactory) { 361 this.cacheRegionFactory = cacheRegionFactory; 362 } 363 364 /** 365 * Set the LobHandler to be used by the SessionFactory. 366 * Will be exposed at config time for UserType implementations. 367 * @see #getConfigTimeLobHandler 368 * @see org.hibernate.usertype.UserType 369 * @see org.springframework.orm.hibernate3.support.ClobStringType 370 * @see org.springframework.orm.hibernate3.support.BlobByteArrayType 371 * @see org.springframework.orm.hibernate3.support.BlobSerializableType 372 */ 373 public void setLobHandler(LobHandler lobHandler) { 374 this.lobHandler = lobHandler; 375 } 376 377 /** 378 * Set a Hibernate entity interceptor that allows to inspect and change 379 * property values before writing to and reading from the database. 380 * Will get applied to any new Session created by this factory. 381 * <p>Such an interceptor can either be set at the SessionFactory level, i.e. on 382 * LocalSessionFactoryBean, or at the Session level, i.e. on HibernateTemplate, 383 * HibernateInterceptor, and HibernateTransactionManager. It's preferable to set 384 * it on LocalSessionFactoryBean or HibernateTransactionManager to avoid repeated 385 * configuration and guarantee consistent behavior in transactions. 386 * @see HibernateTemplate#setEntityInterceptor 387 * @see HibernateInterceptor#setEntityInterceptor 388 * @see HibernateTransactionManager#setEntityInterceptor 389 * @see org.hibernate.cfg.Configuration#setInterceptor 390 */ 391 public void setEntityInterceptor(Interceptor entityInterceptor) { 392 this.entityInterceptor = entityInterceptor; 393 } 394 395 /** 396 * Set a Hibernate NamingStrategy for the SessionFactory, determining the 397 * physical column and table names given the info in the mapping document. 398 * @see org.hibernate.cfg.Configuration#setNamingStrategy 399 */ 400 public void setNamingStrategy(NamingStrategy namingStrategy) { 401 this.namingStrategy = namingStrategy; 402 } 403 404 /** 405 * Specify the Hibernate type definitions to register with the SessionFactory, 406 * as Spring TypeDefinitionBean instances. This is an alternative to specifying 407 * <<typedef> elements in Hibernate mapping files. 408 * <p>Unfortunately, Hibernate itself does not define a complete object that 409 * represents a type definition, hence the need for Spring's TypeDefinitionBean. 410 * @see TypeDefinitionBean 411 * @see org.hibernate.cfg.Mappings#addTypeDef(String, String, java.util.Properties) 412 */ 413 public void setTypeDefinitions(TypeDefinitionBean... typeDefinitions) { 414 this.typeDefinitions = typeDefinitions; 415 } 416 417 /** 418 * Specify the Hibernate FilterDefinitions to register with the SessionFactory. 419 * This is an alternative to specifying <<filter-def> elements in 420 * Hibernate mapping files. 421 * <p>Typically, the passed-in FilterDefinition objects will have been defined 422 * as Spring FilterDefinitionFactoryBeans, probably as inner beans within the 423 * LocalSessionFactoryBean definition. 424 * @see FilterDefinitionFactoryBean 425 * @see org.hibernate.cfg.Configuration#addFilterDefinition 426 */ 427 public void setFilterDefinitions(FilterDefinition... filterDefinitions) { 428 this.filterDefinitions = filterDefinitions; 429 } 430 431 /** 432 * Specify the cache strategies for entities (persistent classes or named entities). 433 * This configuration setting corresponds to the <class-cache> entry 434 * in the "hibernate.cfg.xml" configuration format. 435 * <p>For example: 436 * <pre class="code"> 437 * <property name="entityCacheStrategies"> 438 * <props> 439 * <prop key="com.mycompany.Customer">read-write</prop> 440 * <prop key="com.mycompany.Product">read-only,myRegion</prop> 441 * </props> 442 * </property></pre> 443 * @param entityCacheStrategies properties that define entity cache strategies, 444 * with class names as keys and cache concurrency strategies as values 445 * @see org.hibernate.cfg.Configuration#setCacheConcurrencyStrategy(String, String) 446 */ 447 public void setEntityCacheStrategies(Properties entityCacheStrategies) { 448 this.entityCacheStrategies = entityCacheStrategies; 449 } 450 451 /** 452 * Specify the cache strategies for persistent collections (with specific roles). 453 * This configuration setting corresponds to the <collection-cache> entry 454 * in the "hibernate.cfg.xml" configuration format. 455 * <p>For example: 456 * <pre class="code"> 457 * <property name="collectionCacheStrategies"> 458 * <props> 459 * <prop key="com.mycompany.Order.items">read-write</prop> 460 * <prop key="com.mycompany.Product.categories">read-only,myRegion</prop> 461 * </props> 462 * </property></pre> 463 * @param collectionCacheStrategies properties that define collection cache strategies, 464 * with collection roles as keys and cache concurrency strategies as values 465 * @see org.hibernate.cfg.Configuration#setCollectionCacheConcurrencyStrategy(String, String) 466 */ 467 public void setCollectionCacheStrategies(Properties collectionCacheStrategies) { 468 this.collectionCacheStrategies = collectionCacheStrategies; 469 } 470 471 /** 472 * Specify the Hibernate event listeners to register, with listener types 473 * as keys and listener objects as values. Instead of a single listener object, 474 * you can also pass in a list or set of listeners objects as value. 475 * <p>See the Hibernate documentation for further details on listener types 476 * and associated listener interfaces. 477 * <p>See {@code org.hibernate.cfg.Configuration#setListener(String, Object)} 478 * @param eventListeners Map with listener type Strings as keys and 479 * listener objects as values 480 */ 481 public void setEventListeners(Map<String, Object> eventListeners) { 482 this.eventListeners = eventListeners; 483 } 484 485 /** 486 * Set whether to execute a schema update after SessionFactory initialization. 487 * <p>For details on how to make schema update scripts work, see the Hibernate 488 * documentation, as this class leverages the same schema update script support 489 * in org.hibernate.cfg.Configuration as Hibernate's own SchemaUpdate tool. 490 * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript 491 * @see org.hibernate.tool.hbm2ddl.SchemaUpdate 492 */ 493 public void setSchemaUpdate(boolean schemaUpdate) { 494 this.schemaUpdate = schemaUpdate; 495 } 496 497 @Override 498 public void setBeanClassLoader(ClassLoader beanClassLoader) { 499 this.beanClassLoader = beanClassLoader; 500 } 501 502 503 @Override 504 @SuppressWarnings("unchecked") 505 protected SessionFactory buildSessionFactory() throws Exception { 506 // Create Configuration instance. 507 Configuration config = newConfiguration(); 508 509 DataSource dataSource = getDataSource(); 510 if (dataSource != null) { 511 // Make given DataSource available for SessionFactory configuration. 512 configTimeDataSourceHolder.set(dataSource); 513 } 514 if (this.jtaTransactionManager != null) { 515 // Make Spring-provided JTA TransactionManager available. 516 configTimeTransactionManagerHolder.set(this.jtaTransactionManager); 517 } 518 if (this.cacheRegionFactory != null) { 519 // Make Spring-provided Hibernate RegionFactory available. 520 configTimeRegionFactoryHolder.set(this.cacheRegionFactory); 521 } 522 if (this.lobHandler != null) { 523 // Make given LobHandler available for SessionFactory configuration. 524 // Do early because mapping resource might refer to custom types. 525 configTimeLobHandlerHolder.set(this.lobHandler); 526 } 527 528 // Analogous to Hibernate EntityManager's Ejb3Configuration: 529 // Hibernate doesn't allow setting the bean ClassLoader explicitly, 530 // so we need to expose it as thread context ClassLoader accordingly. 531 Thread currentThread = Thread.currentThread(); 532 ClassLoader threadContextClassLoader = currentThread.getContextClassLoader(); 533 boolean overrideClassLoader = 534 (this.beanClassLoader != null && !this.beanClassLoader.equals(threadContextClassLoader)); 535 if (overrideClassLoader) { 536 currentThread.setContextClassLoader(this.beanClassLoader); 537 } 538 539 try { 540 if (isExposeTransactionAwareSessionFactory()) { 541 // Set Hibernate 3.1+ CurrentSessionContext implementation, 542 // providing the Spring-managed Session as current Session. 543 // Can be overridden by a custom value for the corresponding Hibernate property. 544 config.setProperty( 545 Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName()); 546 } 547 548 if (this.jtaTransactionManager != null) { 549 // Set Spring-provided JTA TransactionManager as Hibernate property. 550 config.setProperty( 551 Environment.TRANSACTION_STRATEGY, JTATransactionFactory.class.getName()); 552 config.setProperty( 553 Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName()); 554 } 555 else { 556 // Makes the Hibernate Session aware of the presence of a Spring-managed transaction. 557 // Also sets connection release mode to ON_CLOSE by default. 558 config.setProperty( 559 Environment.TRANSACTION_STRATEGY, SpringTransactionFactory.class.getName()); 560 } 561 562 if (this.entityInterceptor != null) { 563 // Set given entity interceptor at SessionFactory level. 564 config.setInterceptor(this.entityInterceptor); 565 } 566 567 if (this.namingStrategy != null) { 568 // Pass given naming strategy to Hibernate Configuration. 569 config.setNamingStrategy(this.namingStrategy); 570 } 571 572 if (this.typeDefinitions != null) { 573 // Register specified Hibernate type definitions. 574 Mappings mappings = config.createMappings(); 575 for (TypeDefinitionBean typeDef : this.typeDefinitions) { 576 mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters()); 577 } 578 } 579 580 if (this.filterDefinitions != null) { 581 // Register specified Hibernate FilterDefinitions. 582 for (FilterDefinition filterDef : this.filterDefinitions) { 583 config.addFilterDefinition(filterDef); 584 } 585 } 586 587 if (this.configLocations != null) { 588 for (Resource resource : this.configLocations) { 589 // Load Hibernate configuration from given location. 590 config.configure(resource.getURL()); 591 } 592 } 593 594 if (this.hibernateProperties != null) { 595 // Add given Hibernate properties to Configuration. 596 config.addProperties(this.hibernateProperties); 597 } 598 599 if (dataSource != null) { 600 Class<?> providerClass = LocalDataSourceConnectionProvider.class; 601 if (isUseTransactionAwareDataSource() || dataSource instanceof TransactionAwareDataSourceProxy) { 602 providerClass = TransactionAwareDataSourceConnectionProvider.class; 603 } 604 else if (config.getProperty(Environment.TRANSACTION_MANAGER_STRATEGY) != null) { 605 providerClass = LocalJtaDataSourceConnectionProvider.class; 606 } 607 // Set Spring-provided DataSource as Hibernate ConnectionProvider. 608 config.setProperty(Environment.CONNECTION_PROVIDER, providerClass.getName()); 609 } 610 611 if (this.cacheRegionFactory != null) { 612 // Expose Spring-provided Hibernate RegionFactory. 613 config.setProperty(Environment.CACHE_REGION_FACTORY, LocalRegionFactoryProxy.class.getName()); 614 } 615 616 if (this.mappingResources != null) { 617 // Register given Hibernate mapping definitions, contained in resource files. 618 for (String mapping : this.mappingResources) { 619 Resource resource = new ClassPathResource(mapping.trim(), this.beanClassLoader); 620 config.addInputStream(resource.getInputStream()); 621 } 622 } 623 624 if (this.mappingLocations != null) { 625 // Register given Hibernate mapping definitions, contained in resource files. 626 for (Resource resource : this.mappingLocations) { 627 config.addInputStream(resource.getInputStream()); 628 } 629 } 630 631 if (this.cacheableMappingLocations != null) { 632 // Register given cacheable Hibernate mapping definitions, read from the file system. 633 for (Resource resource : this.cacheableMappingLocations) { 634 config.addCacheableFile(resource.getFile()); 635 } 636 } 637 638 if (this.mappingJarLocations != null) { 639 // Register given Hibernate mapping definitions, contained in jar files. 640 for (Resource resource : this.mappingJarLocations) { 641 config.addJar(resource.getFile()); 642 } 643 } 644 645 if (this.mappingDirectoryLocations != null) { 646 // Register all Hibernate mapping definitions in the given directories. 647 for (Resource resource : this.mappingDirectoryLocations) { 648 File file = resource.getFile(); 649 if (!file.isDirectory()) { 650 throw new IllegalArgumentException( 651 "Mapping directory location [" + resource + "] does not denote a directory"); 652 } 653 config.addDirectory(file); 654 } 655 } 656 657 // Tell Hibernate to eagerly compile the mappings that we registered, 658 // for availability of the mapping information in further processing. 659 postProcessMappings(config); 660 config.buildMappings(); 661 662 if (this.entityCacheStrategies != null) { 663 // Register cache strategies for mapped entities. 664 for (Enumeration<?> classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) { 665 String className = (String) classNames.nextElement(); 666 String[] strategyAndRegion = 667 StringUtils.commaDelimitedListToStringArray(this.entityCacheStrategies.getProperty(className)); 668 if (strategyAndRegion.length > 1) { 669 config.setCacheConcurrencyStrategy(className, strategyAndRegion[0], strategyAndRegion[1]); 670 } 671 else if (strategyAndRegion.length > 0) { 672 config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]); 673 } 674 } 675 } 676 677 if (this.collectionCacheStrategies != null) { 678 // Register cache strategies for mapped collections. 679 for (Enumeration<?> collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) { 680 String collRole = (String) collRoles.nextElement(); 681 String[] strategyAndRegion = 682 StringUtils.commaDelimitedListToStringArray(this.collectionCacheStrategies.getProperty(collRole)); 683 if (strategyAndRegion.length > 1) { 684 config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0], strategyAndRegion[1]); 685 } 686 else if (strategyAndRegion.length > 0) { 687 config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]); 688 } 689 } 690 } 691 692 if (this.eventListeners != null) { 693 // Register specified Hibernate event listeners. 694 for (Map.Entry<String, Object> entry : this.eventListeners.entrySet()) { 695 String listenerType = entry.getKey(); 696 Object listenerObject = entry.getValue(); 697 if (listenerObject instanceof Collection) { 698 Collection<Object> listeners = (Collection<Object>) listenerObject; 699 EventListeners listenerRegistry = config.getEventListeners(); 700 Object[] listenerArray = 701 (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size()); 702 listenerArray = listeners.toArray(listenerArray); 703 config.setListeners(listenerType, listenerArray); 704 } 705 else { 706 config.setListener(listenerType, listenerObject); 707 } 708 } 709 } 710 711 // Perform custom post-processing in subclasses. 712 postProcessConfiguration(config); 713 714 // Build SessionFactory instance. 715 logger.info("Building new Hibernate SessionFactory"); 716 this.configuration = config; 717 return newSessionFactory(config); 718 } 719 720 finally { 721 if (dataSource != null) { 722 configTimeDataSourceHolder.remove(); 723 } 724 if (this.jtaTransactionManager != null) { 725 configTimeTransactionManagerHolder.remove(); 726 } 727 if (this.cacheRegionFactory != null) { 728 configTimeRegionFactoryHolder.remove(); 729 } 730 if (this.lobHandler != null) { 731 configTimeLobHandlerHolder.remove(); 732 } 733 if (overrideClassLoader) { 734 // Reset original thread context ClassLoader. 735 currentThread.setContextClassLoader(threadContextClassLoader); 736 } 737 } 738 } 739 740 /** 741 * Subclasses can override this method to perform custom initialization 742 * of the Configuration instance used for SessionFactory creation. 743 * The properties of this LocalSessionFactoryBean will be applied to 744 * the Configuration object that gets returned here. 745 * <p>The default implementation creates a new Configuration instance. 746 * A custom implementation could prepare the instance in a specific way, 747 * or use a custom Configuration subclass. 748 * @return the Configuration instance 749 * @throws HibernateException in case of Hibernate initialization errors 750 * @see org.hibernate.cfg.Configuration#Configuration() 751 */ 752 protected Configuration newConfiguration() throws HibernateException { 753 return BeanUtils.instantiateClass(this.configurationClass); 754 } 755 756 /** 757 * To be implemented by subclasses that want to register further mappings 758 * on the Configuration object after this FactoryBean registered its specified 759 * mappings. 760 * <p>Invoked <i>before</i> the {@code Configuration.buildMappings()} call, 761 * so that it can still extend and modify the mapping information. 762 * @param config the current Configuration object 763 * @throws HibernateException in case of Hibernate initialization errors 764 * @see org.hibernate.cfg.Configuration#buildMappings() 765 */ 766 protected void postProcessMappings(Configuration config) throws HibernateException { 767 } 768 769 /** 770 * To be implemented by subclasses that want to perform custom 771 * post-processing of the Configuration object after this FactoryBean 772 * performed its default initialization. 773 * <p>Invoked <i>after</i> the {@code Configuration.buildMappings()} call, 774 * so that it can operate on the completed and fully parsed mapping information. 775 * @param config the current Configuration object 776 * @throws HibernateException in case of Hibernate initialization errors 777 * @see org.hibernate.cfg.Configuration#buildMappings() 778 */ 779 protected void postProcessConfiguration(Configuration config) throws HibernateException { 780 } 781 782 /** 783 * Subclasses can override this method to perform custom initialization 784 * of the SessionFactory instance, creating it via the given Configuration 785 * object that got prepared by this LocalSessionFactoryBean. 786 * <p>The default implementation invokes Configuration's buildSessionFactory. 787 * A custom implementation could prepare the instance in a specific way, 788 * or use a custom SessionFactoryImpl subclass. 789 * @param config Configuration prepared by this LocalSessionFactoryBean 790 * @return the SessionFactory instance 791 * @throws HibernateException in case of Hibernate initialization errors 792 * @see org.hibernate.cfg.Configuration#buildSessionFactory 793 */ 794 protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { 795 return config.buildSessionFactory(); 796 } 797 798 /** 799 * Return the Configuration object used to build the SessionFactory. 800 * Allows for access to configuration metadata stored there (rarely needed). 801 * @throws IllegalStateException if the Configuration object has not been initialized yet 802 */ 803 public final Configuration getConfiguration() { 804 if (this.configuration == null) { 805 throw new IllegalStateException("Configuration not initialized yet"); 806 } 807 return this.configuration; 808 } 809 810 /** 811 * Executes schema update if requested. 812 * @see #setSchemaUpdate 813 * @see #updateDatabaseSchema() 814 */ 815 @Override 816 protected void afterSessionFactoryCreation() throws Exception { 817 if (this.schemaUpdate) { 818 updateDatabaseSchema(); 819 } 820 } 821 822 /** 823 * Allows for schema export on shutdown. 824 */ 825 @Override 826 public void destroy() throws HibernateException { 827 DataSource dataSource = getDataSource(); 828 if (dataSource != null) { 829 // Make given DataSource available for potential SchemaExport, 830 // which unfortunately reinstantiates a ConnectionProvider. 831 configTimeDataSourceHolder.set(dataSource); 832 } 833 try { 834 super.destroy(); 835 } 836 finally { 837 if (dataSource != null) { 838 // Reset DataSource holder. 839 configTimeDataSourceHolder.remove(); 840 } 841 } 842 } 843 844 845 /** 846 * Execute schema update script, determined by the Configuration object 847 * used for creating the SessionFactory. A replacement for Hibernate's 848 * SchemaUpdate class, for automatically executing schema update scripts 849 * on application startup. Can also be invoked manually. 850 * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed 851 * SessionFactory to be able to invoke this method, e.g. via 852 * {@code LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");}. 853 * <p>Uses the SessionFactory that this bean generates for accessing a 854 * JDBC connection to perform the script. 855 * @throws DataAccessException in case of script execution errors 856 * @see #setSchemaUpdate 857 * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript 858 * @see org.hibernate.tool.hbm2ddl.SchemaUpdate 859 */ 860 public void updateDatabaseSchema() throws DataAccessException { 861 logger.info("Updating database schema for Hibernate SessionFactory"); 862 DataSource dataSource = getDataSource(); 863 if (dataSource != null) { 864 // Make given DataSource available for the schema update. 865 configTimeDataSourceHolder.set(dataSource); 866 } 867 try { 868 SessionFactory sessionFactory = getSessionFactory(); 869 final Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect(); 870 HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory); 871 hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); 872 hibernateTemplate.execute( 873 new HibernateCallback<Object>() { 874 @Override 875 public Object doInHibernate(Session session) throws HibernateException, SQLException { 876 Connection con = session.connection(); 877 DatabaseMetadata metadata = new DatabaseMetadata(con, dialect); 878 String[] sql = getConfiguration().generateSchemaUpdateScript(dialect, metadata); 879 executeSchemaScript(con, sql); 880 return null; 881 } 882 } 883 ); 884 } 885 finally { 886 if (dataSource != null) { 887 configTimeDataSourceHolder.remove(); 888 } 889 } 890 } 891 892 /** 893 * Execute schema creation script, determined by the Configuration object 894 * used for creating the SessionFactory. A replacement for Hibernate's 895 * SchemaValidator class, to be invoked after application startup. 896 * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed 897 * SessionFactory to be able to invoke this method, e.g. via 898 * {@code LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");}. 899 * <p>Uses the SessionFactory that this bean generates for accessing a 900 * JDBC connection to perform the script. 901 * @throws DataAccessException in case of script execution errors 902 * @see org.hibernate.cfg.Configuration#validateSchema 903 * @see org.hibernate.tool.hbm2ddl.SchemaValidator 904 */ 905 public void validateDatabaseSchema() throws DataAccessException { 906 logger.info("Validating database schema for Hibernate SessionFactory"); 907 DataSource dataSource = getDataSource(); 908 if (dataSource != null) { 909 // Make given DataSource available for the schema update. 910 configTimeDataSourceHolder.set(dataSource); 911 } 912 try { 913 SessionFactory sessionFactory = getSessionFactory(); 914 final Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect(); 915 HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory); 916 hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); 917 hibernateTemplate.execute( 918 new HibernateCallback<Object>() { 919 @Override 920 public Object doInHibernate(Session session) throws HibernateException, SQLException { 921 Connection con = session.connection(); 922 DatabaseMetadata metadata = new DatabaseMetadata(con, dialect, false); 923 getConfiguration().validateSchema(dialect, metadata); 924 return null; 925 } 926 } 927 ); 928 } 929 finally { 930 if (dataSource != null) { 931 configTimeDataSourceHolder.remove(); 932 } 933 } 934 } 935 936 /** 937 * Execute schema drop script, determined by the Configuration object 938 * used for creating the SessionFactory. A replacement for Hibernate's 939 * SchemaExport class, to be invoked on application setup. 940 * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed 941 * SessionFactory to be able to invoke this method, e.g. via 942 * {@code LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");}. 943 * <p>Uses the SessionFactory that this bean generates for accessing a 944 * JDBC connection to perform the script. 945 * @throws org.springframework.dao.DataAccessException in case of script execution errors 946 * @see org.hibernate.cfg.Configuration#generateDropSchemaScript 947 * @see org.hibernate.tool.hbm2ddl.SchemaExport#drop 948 */ 949 public void dropDatabaseSchema() throws DataAccessException { 950 logger.info("Dropping database schema for Hibernate SessionFactory"); 951 SessionFactory sessionFactory = getSessionFactory(); 952 final Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect(); 953 HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory); 954 hibernateTemplate.execute( 955 new HibernateCallback<Object>() { 956 @Override 957 public Object doInHibernate(Session session) throws HibernateException, SQLException { 958 Connection con = session.connection(); 959 String[] sql = getConfiguration().generateDropSchemaScript(dialect); 960 executeSchemaScript(con, sql); 961 return null; 962 } 963 } 964 ); 965 } 966 967 /** 968 * Execute schema creation script, determined by the Configuration object 969 * used for creating the SessionFactory. A replacement for Hibernate's 970 * SchemaExport class, to be invoked on application setup. 971 * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed 972 * SessionFactory to be able to invoke this method, e.g. via 973 * {@code LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");}. 974 * <p>Uses the SessionFactory that this bean generates for accessing a 975 * JDBC connection to perform the script. 976 * @throws DataAccessException in case of script execution errors 977 * @see org.hibernate.cfg.Configuration#generateSchemaCreationScript 978 * @see org.hibernate.tool.hbm2ddl.SchemaExport#create 979 */ 980 public void createDatabaseSchema() throws DataAccessException { 981 logger.info("Creating database schema for Hibernate SessionFactory"); 982 DataSource dataSource = getDataSource(); 983 if (dataSource != null) { 984 // Make given DataSource available for the schema update. 985 configTimeDataSourceHolder.set(dataSource); 986 } 987 try { 988 SessionFactory sessionFactory = getSessionFactory(); 989 final Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect(); 990 HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory); 991 hibernateTemplate.execute( 992 new HibernateCallback<Object>() { 993 @Override 994 public Object doInHibernate(Session session) throws HibernateException, SQLException { 995 Connection con = session.connection(); 996 String[] sql = getConfiguration().generateSchemaCreationScript(dialect); 997 executeSchemaScript(con, sql); 998 return null; 999 } 1000 } 1001 ); 1002 } 1003 finally { 1004 if (dataSource != null) { 1005 configTimeDataSourceHolder.remove(); 1006 } 1007 } 1008 } 1009 1010 /** 1011 * Execute the given schema script on the given JDBC Connection. 1012 * <p>Note that the default implementation will log unsuccessful statements 1013 * and continue to execute. Override the {@code executeSchemaStatement} 1014 * method to treat failures differently. 1015 * @param con the JDBC Connection to execute the script on 1016 * @param sql the SQL statements to execute 1017 * @throws SQLException if thrown by JDBC methods 1018 * @see #executeSchemaStatement 1019 */ 1020 protected void executeSchemaScript(Connection con, String[] sql) throws SQLException { 1021 if (sql != null && sql.length > 0) { 1022 boolean oldAutoCommit = con.getAutoCommit(); 1023 if (!oldAutoCommit) { 1024 con.setAutoCommit(true); 1025 } 1026 try { 1027 Statement stmt = con.createStatement(); 1028 try { 1029 for (String sqlStmt : sql) { 1030 executeSchemaStatement(stmt, sqlStmt); 1031 } 1032 } 1033 finally { 1034 JdbcUtils.closeStatement(stmt); 1035 } 1036 } 1037 finally { 1038 if (!oldAutoCommit) { 1039 con.setAutoCommit(false); 1040 } 1041 } 1042 } 1043 } 1044 1045 /** 1046 * Execute the given schema SQL on the given JDBC Statement. 1047 * <p>Note that the default implementation will log unsuccessful statements 1048 * and continue to execute. Override this method to treat failures differently. 1049 * @param stmt the JDBC Statement to execute the SQL on 1050 * @param sql the SQL statement to execute 1051 * @throws SQLException if thrown by JDBC methods (and considered fatal) 1052 */ 1053 protected void executeSchemaStatement(Statement stmt, String sql) throws SQLException { 1054 if (logger.isDebugEnabled()) { 1055 logger.debug("Executing schema statement: " + sql); 1056 } 1057 try { 1058 stmt.executeUpdate(sql); 1059 } 1060 catch (SQLException ex) { 1061 if (logger.isWarnEnabled()) { 1062 logger.warn("Unsuccessful schema statement: " + sql, ex); 1063 } 1064 } 1065 } 1066 1067}