001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.orm.hibernate;
018:
019: import java.io.File;
020: import java.io.IOException;
021: import java.sql.Connection;
022: import java.sql.SQLException;
023: import java.sql.Statement;
024: import java.util.Properties;
025:
026: import javax.sql.DataSource;
027: import javax.transaction.TransactionManager;
028:
029: import net.sf.hibernate.HibernateException;
030: import net.sf.hibernate.Interceptor;
031: import net.sf.hibernate.Session;
032: import net.sf.hibernate.SessionFactory;
033: import net.sf.hibernate.cfg.Configuration;
034: import net.sf.hibernate.cfg.Environment;
035: import net.sf.hibernate.cfg.NamingStrategy;
036: import net.sf.hibernate.dialect.Dialect;
037: import net.sf.hibernate.tool.hbm2ddl.DatabaseMetadata;
038: import org.apache.commons.logging.Log;
039: import org.apache.commons.logging.LogFactory;
040:
041: import org.springframework.beans.factory.DisposableBean;
042: import org.springframework.beans.factory.FactoryBean;
043: import org.springframework.beans.factory.InitializingBean;
044: import org.springframework.core.io.ClassPathResource;
045: import org.springframework.core.io.Resource;
046: import org.springframework.dao.DataAccessException;
047: import org.springframework.jdbc.support.JdbcUtils;
048: import org.springframework.jdbc.support.lob.LobHandler;
049:
050: /**
051: * {@link org.springframework.beans.factory.FactoryBean} that creates a
052: * Hibernate {@link net.sf.hibernate.SessionFactory}. This is the usual way to
053: * set up a shared Hibernate SessionFactory in a Spring application context;
054: * the SessionFactory can then be passed to Hibernate-based DAOs via
055: * dependency injection.
056: *
057: * <p>Configuration settings can either be read from a Hibernate XML file,
058: * specified as "configLocation", or completely via this class. A typical
059: * local configuration consists of one or more "mappingResources", various
060: * "hibernateProperties" (not strictly necessary), and a "dataSource" that the
061: * SessionFactory should use. The latter can also be specified via Hibernate
062: * properties, but "dataSource" supports any Spring-configured DataSource,
063: * instead of relying on Hibernate's own connection providers.
064: *
065: * <p>This SessionFactory handling strategy is appropriate for most types of
066: * applications, from Hibernate-only single database apps to ones that need
067: * distributed transactions. Either {@link HibernateTransactionManager} or
068: * {@link org.springframework.transaction.jta.JtaTransactionManager} can be
069: * used for transaction demarcation, with the latter only necessary for
070: * transactions which span multiple databases.
071: *
072: * <p>Note: Spring's Hibernate support in this package requires Hibernate 2.1.
073: * Dedicated Hibernate3 support can be found in a separate package:
074: * <code>org.springframework.orm.hibernate3</code>.
075: *
076: * @author Juergen Hoeller
077: * @since 05.05.2003
078: * @see HibernateTemplate#setSessionFactory
079: * @see HibernateTransactionManager#setSessionFactory
080: */
081: public class LocalSessionFactoryBean implements FactoryBean,
082: InitializingBean, DisposableBean {
083:
084: private static final ThreadLocal configTimeDataSourceHolder = new ThreadLocal();
085:
086: private static final ThreadLocal configTimeTransactionManagerHolder = new ThreadLocal();
087:
088: private static final ThreadLocal configTimeLobHandlerHolder = new ThreadLocal();
089:
090: /**
091: * Return the DataSource for the currently configured Hibernate SessionFactory,
092: * to be used by LocalDataSourceConnectionProvoder.
093: * <p>This instance will be set before initialization of the corresponding
094: * SessionFactory, and reset immediately afterwards. It is thus only available
095: * during configuration.
096: * @see #setDataSource
097: * @see LocalDataSourceConnectionProvider
098: */
099: public static DataSource getConfigTimeDataSource() {
100: return (DataSource) configTimeDataSourceHolder.get();
101: }
102:
103: /**
104: * Return the JTA TransactionManager for the currently configured Hibernate
105: * SessionFactory, to be used by LocalTransactionManagerLookup.
106: * <p>This instance will be set before initialization of the corresponding
107: * SessionFactory, and reset immediately afterwards. It is thus only available
108: * during configuration.
109: * @see #setJtaTransactionManager
110: * @see LocalTransactionManagerLookup
111: */
112: public static TransactionManager getConfigTimeTransactionManager() {
113: return (TransactionManager) configTimeTransactionManagerHolder
114: .get();
115: }
116:
117: /**
118: * Return the LobHandler for the currently configured Hibernate SessionFactory,
119: * to be used by UserType implementations like ClobStringType.
120: * <p>This instance will be set before initialization of the corresponding
121: * SessionFactory, and reset immediately afterwards. It is thus only available
122: * during configuration.
123: * @see #setLobHandler
124: * @see org.springframework.orm.hibernate.support.ClobStringType
125: * @see org.springframework.orm.hibernate.support.BlobByteArrayType
126: * @see org.springframework.orm.hibernate.support.BlobSerializableType
127: */
128: public static LobHandler getConfigTimeLobHandler() {
129: return (LobHandler) configTimeLobHandlerHolder.get();
130: }
131:
132: protected final Log logger = LogFactory.getLog(getClass());
133:
134: private Resource configLocation;
135:
136: private Resource[] mappingLocations;
137:
138: private Resource[] mappingJarLocations;
139:
140: private Resource[] mappingDirectoryLocations;
141:
142: private Properties hibernateProperties;
143:
144: private DataSource dataSource;
145:
146: private boolean useTransactionAwareDataSource = false;
147:
148: private TransactionManager jtaTransactionManager;
149:
150: private LobHandler lobHandler;
151:
152: private Interceptor entityInterceptor;
153:
154: private NamingStrategy namingStrategy;
155:
156: private boolean schemaUpdate = false;
157:
158: private Configuration configuration;
159:
160: private SessionFactory sessionFactory;
161:
162: /**
163: * Set the location of the Hibernate XML config file, for example as
164: * classpath resource "classpath:hibernate.cfg.xml".
165: * <p>Note: Can be omitted when all necessary properties and mapping
166: * resources are specified locally via this bean.
167: * @see net.sf.hibernate.cfg.Configuration#configure(java.net.URL)
168: */
169: public void setConfigLocation(Resource configLocation) {
170: this .configLocation = configLocation;
171: }
172:
173: /**
174: * Set Hibernate mapping resources to be found in the class path,
175: * like "example.hbm.xml" or "mypackage/example.hbm.xml".
176: * Analogous to mapping entries in a Hibernate XML config file.
177: * Alternative to the more generic setMappingLocations method.
178: * <p>Can be used to add to mappings from a Hibernate XML config file,
179: * or to specify all mappings locally.
180: * @see #setMappingLocations
181: * @see net.sf.hibernate.cfg.Configuration#addResource
182: */
183: public void setMappingResources(String[] mappingResources) {
184: this .mappingLocations = new Resource[mappingResources.length];
185: for (int i = 0; i < mappingResources.length; i++) {
186: this .mappingLocations[i] = new ClassPathResource(
187: mappingResources[i].trim());
188: }
189: }
190:
191: /**
192: * Set locations of Hibernate mapping files, for example as classpath
193: * resource "classpath:example.hbm.xml". Supports any resource location
194: * via Spring's resource abstraction, for example relative paths like
195: * "WEB-INF/mappings/example.hbm.xml" when running in an application context.
196: * <p>Can be used to add to mappings from a Hibernate XML config file,
197: * or to specify all mappings locally.
198: * @see net.sf.hibernate.cfg.Configuration#addInputStream
199: */
200: public void setMappingLocations(Resource[] mappingLocations) {
201: this .mappingLocations = mappingLocations;
202: }
203:
204: /**
205: * Set locations of jar files that contain Hibernate mapping resources,
206: * like "WEB-INF/lib/example.hbm.jar".
207: * <p>Can be used to add to mappings from a Hibernate XML config file,
208: * or to specify all mappings locally.
209: * @see net.sf.hibernate.cfg.Configuration#addJar(java.io.File)
210: */
211: public void setMappingJarLocations(Resource[] mappingJarLocations) {
212: this .mappingJarLocations = mappingJarLocations;
213: }
214:
215: /**
216: * Set locations of directories that contain Hibernate mapping resources,
217: * like "WEB-INF/mappings".
218: * <p>Can be used to add to mappings from a Hibernate XML config file,
219: * or to specify all mappings locally.
220: * @see net.sf.hibernate.cfg.Configuration#addDirectory(java.io.File)
221: */
222: public void setMappingDirectoryLocations(
223: Resource[] mappingDirectoryLocations) {
224: this .mappingDirectoryLocations = mappingDirectoryLocations;
225: }
226:
227: /**
228: * Set Hibernate properties, such as "hibernate.dialect".
229: * <p>Can be used to override values in a Hibernate XML config file,
230: * or to specify all necessary properties locally.
231: * <p>Note: Do not specify a transaction provider here when using
232: * Spring-driven transactions. It is also advisable to omit connection
233: * provider settings and use a Spring-set DataSource instead.
234: * @see #setDataSource
235: */
236: public void setHibernateProperties(Properties hibernateProperties) {
237: this .hibernateProperties = hibernateProperties;
238: }
239:
240: /**
241: * Return the Hibernate properties, if any. Mainly available for
242: * configuration through property paths that specify individual keys.
243: */
244: public Properties getHibernateProperties() {
245: if (this .hibernateProperties == null) {
246: this .hibernateProperties = new Properties();
247: }
248: return this .hibernateProperties;
249: }
250:
251: /**
252: * Set the DataSource to be used by the SessionFactory.
253: * If set, this will override corresponding settings in Hibernate properties.
254: * <p>Note: If this is set, the Hibernate settings should not define
255: * a connection provider to avoid meaningless double configuration.
256: * <p>If using HibernateTransactionManager as transaction strategy, consider
257: * proxying your target DataSource with a LazyConnectionDataSourceProxy.
258: * This defers fetching of an actual JDBC Connection until the first JDBC
259: * Statement gets executed, even within JDBC transactions (as performed by
260: * HibernateTransactionManager). Such lazy fetching is particularly beneficial
261: * for read-only operations, in particular if the chances of resolving the
262: * result in the second-level cache are high.
263: * <p>As JTA and transactional JNDI DataSources already provide lazy enlistment
264: * of JDBC Connections, LazyConnectionDataSourceProxy does not add value with
265: * JTA (i.e. Spring's JtaTransactionManager) as transaction strategy.
266: * @see #setUseTransactionAwareDataSource
267: * @see LocalDataSourceConnectionProvider
268: * @see HibernateTransactionManager
269: * @see org.springframework.transaction.jta.JtaTransactionManager
270: * @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
271: */
272: public void setDataSource(DataSource dataSource) {
273: this .dataSource = dataSource;
274: }
275:
276: /**
277: * Set whether to use a transaction-aware DataSource for the SessionFactory,
278: * i.e. whether to automatically wrap the passed-in DataSource with Spring's
279: * TransactionAwareDataSourceProxy.
280: * <p>Default is "false": LocalSessionFactoryBean is usually used with Spring's
281: * HibernateTransactionManager or JtaTransactionManager, both of which work nicely
282: * on a plain JDBC DataSource. Hibernate Sessions and their JDBC Connections are
283: * fully managed by the Hibernate/JTA transaction infrastructure in such a scenario.
284: * <p>If you switch this flag to "true", Spring's Hibernate access will be able to
285: * <i>participate in JDBC-based transactions managed outside of Hibernate</i>
286: * (for example, by Spring's DataSourceTransactionManager). This can be convenient
287: * if you need a different local transaction strategy for another O/R mapping tool,
288: * for example, but still want Hibernate access to join into those transactions.
289: * <p>A further benefit of this option is that <i>plain Sessions opened directly
290: * via the SessionFactory</i>, outside of Spring's Hibernate support, will still
291: * participate in active Spring-managed transactions.
292: * <p>As a further effect, using a transaction-aware DataSource will <i>apply
293: * remaining transaction timeouts to all created JDBC Statements</i>. This means
294: * that all operations performed by the SessionFactory will automatically
295: * participate in Spring-managed transaction timeouts, not just queries.
296: * This adds value even for HibernateTransactionManager.
297: * <p><b>WARNING: Be aware of side effects when using a transaction-aware
298: * DataSource in combination with OpenSessionInViewFilter/Interceptor.</b>
299: * This combination is only properly supported with HibernateTransactionManager
300: * transactions. PROPAGATION_SUPPORTS with HibernateTransactionManager and
301: * JtaTransactionManager in general are only supported on Hibernate3, which
302: * introduces (optional) aggressive release of Connections.
303: * @see #setDataSource
304: * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
305: * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
306: * @see org.springframework.orm.hibernate.support.OpenSessionInViewFilter
307: * @see org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor
308: * @see HibernateTransactionManager
309: * @see org.springframework.transaction.jta.JtaTransactionManager
310: * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setUseTransactionAwareDataSource
311: */
312: public void setUseTransactionAwareDataSource(
313: boolean useTransactionAwareDataSource) {
314: this .useTransactionAwareDataSource = useTransactionAwareDataSource;
315: }
316:
317: /**
318: * Set the JTA TransactionManager to be used for Hibernate's
319: * TransactionManagerLookup. If set, this will override corresponding
320: * settings in Hibernate properties. Allows to use a Spring-managed
321: * JTA TransactionManager for Hibernate's cache synchronization.
322: * <p>Note: If this is set, the Hibernate settings should not define a
323: * transaction manager lookup to avoid meaningless double configuration.
324: * @see LocalTransactionManagerLookup
325: */
326: public void setJtaTransactionManager(
327: TransactionManager jtaTransactionManager) {
328: this .jtaTransactionManager = jtaTransactionManager;
329: }
330:
331: /**
332: * Set the LobHandler to be used by the SessionFactory.
333: * Will be exposed at config time for UserType implementations.
334: * @see #getConfigTimeLobHandler
335: * @see net.sf.hibernate.UserType
336: * @see org.springframework.orm.hibernate.support.ClobStringType
337: * @see org.springframework.orm.hibernate.support.BlobByteArrayType
338: * @see org.springframework.orm.hibernate.support.BlobSerializableType
339: */
340: public void setLobHandler(LobHandler lobHandler) {
341: this .lobHandler = lobHandler;
342: }
343:
344: /**
345: * Set a Hibernate entity interceptor that allows to inspect and change
346: * property values before writing to and reading from the database.
347: * Will get applied to any new Session created by this factory.
348: * <p>Such an interceptor can either be set at the SessionFactory level, i.e. on
349: * LocalSessionFactoryBean, or at the Session level, i.e. on HibernateTemplate,
350: * HibernateInterceptor, and HibernateTransactionManager. It's preferable to set
351: * it on LocalSessionFactoryBean or HibernateTransactionManager to avoid repeated
352: * configuration and guarantee consistent behavior in transactions.
353: * @see HibernateTemplate#setEntityInterceptor
354: * @see HibernateInterceptor#setEntityInterceptor
355: * @see HibernateTransactionManager#setEntityInterceptor
356: * @see net.sf.hibernate.cfg.Configuration#setInterceptor
357: */
358: public void setEntityInterceptor(Interceptor entityInterceptor) {
359: this .entityInterceptor = entityInterceptor;
360: }
361:
362: /**
363: * Set a Hibernate NamingStrategy for the SessionFactory, determining the
364: * physical column and table names given the info in the mapping document.
365: * @see net.sf.hibernate.cfg.Configuration#setNamingStrategy
366: */
367: public void setNamingStrategy(NamingStrategy namingStrategy) {
368: this .namingStrategy = namingStrategy;
369: }
370:
371: /**
372: * Set whether to execute a schema update after SessionFactory initialization.
373: * <p>For details on how to make schema update scripts work, see the Hibernate
374: * documentation, as this class leverages the same schema update script support
375: * in net.sf.hibernate.cfg.Configuration as Hibernate's own SchemaUpdate tool.
376: * @see net.sf.hibernate.cfg.Configuration#generateSchemaUpdateScript
377: * @see net.sf.hibernate.tool.hbm2ddl.SchemaUpdate
378: */
379: public void setSchemaUpdate(boolean schemaUpdate) {
380: this .schemaUpdate = schemaUpdate;
381: }
382:
383: /**
384: * Initialize the SessionFactory for the given or the default location.
385: * @throws IllegalArgumentException in case of illegal property values
386: * @throws HibernateException in case of Hibernate initialization errors
387: */
388: public void afterPropertiesSet() throws IllegalArgumentException,
389: HibernateException, IOException {
390: // Create Configuration instance.
391: Configuration config = newConfiguration();
392:
393: if (this .dataSource != null) {
394: // Make given DataSource available for SessionFactory configuration.
395: configTimeDataSourceHolder.set(this .dataSource);
396: }
397:
398: if (this .jtaTransactionManager != null) {
399: // Make Spring-provided JTA TransactionManager available.
400: configTimeTransactionManagerHolder
401: .set(this .jtaTransactionManager);
402: }
403:
404: if (this .lobHandler != null) {
405: // Make given LobHandler available for SessionFactory configuration.
406: // Do early because because mapping resource might refer to custom types.
407: configTimeLobHandlerHolder.set(this .lobHandler);
408: }
409:
410: try {
411:
412: if (this .entityInterceptor != null) {
413: // Set given entity interceptor at SessionFactory level.
414: config.setInterceptor(this .entityInterceptor);
415: }
416:
417: if (this .namingStrategy != null) {
418: // Pass given naming strategy to Hibernate Configuration.
419: config.setNamingStrategy(this .namingStrategy);
420: }
421:
422: if (this .configLocation != null) {
423: // Load Hibernate configuration from given location.
424: config.configure(this .configLocation.getURL());
425: }
426:
427: if (this .hibernateProperties != null) {
428: // Add given Hibernate properties to Configuration.
429: config.addProperties(this .hibernateProperties);
430: }
431:
432: if (this .dataSource != null) {
433: // Set Spring-provided DataSource as Hibernate property.
434: config
435: .setProperty(
436: Environment.CONNECTION_PROVIDER,
437: this .useTransactionAwareDataSource ? TransactionAwareDataSourceConnectionProvider.class
438: .getName()
439: : LocalDataSourceConnectionProvider.class
440: .getName());
441: }
442:
443: if (this .jtaTransactionManager != null) {
444: // Set Spring-provided JTA TransactionManager as Hibernate property.
445: config.setProperty(
446: Environment.TRANSACTION_MANAGER_STRATEGY,
447: LocalTransactionManagerLookup.class.getName());
448: }
449:
450: if (this .mappingLocations != null) {
451: // Register given Hibernate mapping definitions, contained in resource files.
452: for (int i = 0; i < this .mappingLocations.length; i++) {
453: config.addInputStream(this .mappingLocations[i]
454: .getInputStream());
455: }
456: }
457:
458: if (this .mappingJarLocations != null) {
459: // Register given Hibernate mapping definitions, contained in jar files.
460: for (int i = 0; i < this .mappingJarLocations.length; i++) {
461: Resource resource = this .mappingJarLocations[i];
462: config.addJar(resource.getFile());
463: }
464: }
465:
466: if (this .mappingDirectoryLocations != null) {
467: // Register all Hibernate mapping definitions in the given directories.
468: for (int i = 0; i < this .mappingDirectoryLocations.length; i++) {
469: File file = this .mappingDirectoryLocations[i]
470: .getFile();
471: if (!file.isDirectory()) {
472: throw new IllegalArgumentException(
473: "Mapping directory location ["
474: + this .mappingDirectoryLocations[i]
475: + "] does not denote a directory");
476: }
477: config.addDirectory(file);
478: }
479: }
480:
481: // Perform custom post-processing in subclasses.
482: postProcessConfiguration(config);
483:
484: // Build SessionFactory instance.
485: logger.info("Building new Hibernate SessionFactory");
486: this .configuration = config;
487: this .sessionFactory = newSessionFactory(config);
488: }
489:
490: finally {
491: if (this .dataSource != null) {
492: // Reset DataSource holder.
493: configTimeDataSourceHolder.set(null);
494: }
495:
496: if (this .jtaTransactionManager != null) {
497: // Reset TransactionManager holder.
498: configTimeTransactionManagerHolder.set(null);
499: }
500:
501: if (this .lobHandler != null) {
502: // Reset LobHandler holder.
503: configTimeLobHandlerHolder.set(null);
504: }
505: }
506:
507: // Execute schema update if requested.
508: if (this .schemaUpdate) {
509: updateDatabaseSchema();
510: }
511: }
512:
513: /**
514: * Subclasses can override this method to perform custom initialization
515: * of the Configuration instance used for SessionFactory creation.
516: * The properties of this LocalSessionFactoryBean will be applied to
517: * the Configuration object that gets returned here.
518: * <p>The default implementation creates a new Configuration instance.
519: * A custom implementation could prepare the instance in a specific way,
520: * or use a custom Configuration subclass.
521: * @return the Configuration instance
522: * @throws HibernateException in case of Hibernate initialization errors
523: * @see net.sf.hibernate.cfg.Configuration#Configuration()
524: */
525: protected Configuration newConfiguration()
526: throws HibernateException {
527: return new Configuration();
528: }
529:
530: /**
531: * To be implemented by subclasses that want to to perform custom
532: * post-processing of the Configuration object after this FactoryBean
533: * performed its default initialization.
534: * @param config the current Configuration object
535: * @throws HibernateException in case of Hibernate initialization errors
536: */
537: protected void postProcessConfiguration(Configuration config)
538: throws HibernateException {
539: }
540:
541: /**
542: * Subclasses can override this method to perform custom initialization
543: * of the SessionFactory instance, creating it via the given Configuration
544: * object that got prepared by this LocalSessionFactoryBean.
545: * <p>The default implementation invokes Configuration's buildSessionFactory.
546: * A custom implementation could prepare the instance in a specific way,
547: * or use a custom SessionFactoryImpl subclass.
548: * @param config Configuration prepared by this LocalSessionFactoryBean
549: * @return the SessionFactory instance
550: * @throws HibernateException in case of Hibernate initialization errors
551: * @see net.sf.hibernate.cfg.Configuration#buildSessionFactory
552: */
553: protected SessionFactory newSessionFactory(Configuration config)
554: throws HibernateException {
555: return config.buildSessionFactory();
556: }
557:
558: /**
559: * Execute schema drop script, determined by the Configuration object
560: * used for creating the SessionFactory. A replacement for Hibernate's
561: * SchemaExport class, to be invoked on application setup.
562: * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
563: * SessionFactory to be able to invoke this method, e.g. via
564: * <code>LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");</code>.
565: * <p>Uses the SessionFactory that this bean generates for accessing a JDBC
566: * connection to perform the script.
567: * @throws DataAccessException in case of script execution errors
568: * @see net.sf.hibernate.cfg.Configuration#generateDropSchemaScript
569: * @see net.sf.hibernate.tool.hbm2ddl.SchemaExport#drop
570: */
571: public void dropDatabaseSchema() throws DataAccessException {
572: logger
573: .info("Dropping database schema for Hibernate SessionFactory");
574: HibernateTemplate hibernateTemplate = new HibernateTemplate(
575: this .sessionFactory);
576: hibernateTemplate.execute(new HibernateCallback() {
577: public Object doInHibernate(Session session)
578: throws HibernateException, SQLException {
579: Connection con = session.connection();
580: Dialect dialect = Dialect.getDialect(configuration
581: .getProperties());
582: String[] sql = configuration
583: .generateDropSchemaScript(dialect);
584: executeSchemaScript(con, sql);
585: return null;
586: }
587: });
588: }
589:
590: /**
591: * Execute schema creation script, determined by the Configuration object
592: * used for creating the SessionFactory. A replacement for Hibernate's
593: * SchemaExport class, to be invoked on application setup.
594: * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
595: * SessionFactory to be able to invoke this method, e.g. via
596: * <code>LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");</code>.
597: * <p>Uses the SessionFactory that this bean generates for accessing a JDBC
598: * connection to perform the script.
599: * @throws DataAccessException in case of script execution errors
600: * @see net.sf.hibernate.cfg.Configuration#generateSchemaCreationScript
601: * @see net.sf.hibernate.tool.hbm2ddl.SchemaExport#create
602: */
603: public void createDatabaseSchema() throws DataAccessException {
604: logger
605: .info("Creating database schema for Hibernate SessionFactory");
606: HibernateTemplate hibernateTemplate = new HibernateTemplate(
607: this .sessionFactory);
608: hibernateTemplate.execute(new HibernateCallback() {
609: public Object doInHibernate(Session session)
610: throws HibernateException, SQLException {
611: Connection con = session.connection();
612: Dialect dialect = Dialect.getDialect(configuration
613: .getProperties());
614: String[] sql = configuration
615: .generateSchemaCreationScript(dialect);
616: executeSchemaScript(con, sql);
617: return null;
618: }
619: });
620: }
621:
622: /**
623: * Execute schema update script, determined by the Configuration object
624: * used for creating the SessionFactory. A replacement for Hibernate's
625: * SchemaUpdate class, for automatically executing schema update scripts
626: * on application startup. Can also be invoked manually.
627: * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed
628: * SessionFactory to be able to invoke this method, e.g. via
629: * <code>LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");</code>.
630: * <p>Uses the SessionFactory that this bean generates for accessing a JDBC
631: * connection to perform the script.
632: * @throws DataAccessException in case of script execution errors
633: * @see #setSchemaUpdate
634: * @see net.sf.hibernate.cfg.Configuration#generateSchemaUpdateScript
635: * @see net.sf.hibernate.tool.hbm2ddl.SchemaUpdate
636: */
637: public void updateDatabaseSchema() throws DataAccessException {
638: logger
639: .info("Updating database schema for Hibernate SessionFactory");
640: HibernateTemplate hibernateTemplate = new HibernateTemplate(
641: this .sessionFactory);
642: hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER);
643: hibernateTemplate.execute(new HibernateCallback() {
644: public Object doInHibernate(Session session)
645: throws HibernateException, SQLException {
646: Connection con = session.connection();
647: Dialect dialect = Dialect.getDialect(configuration
648: .getProperties());
649: DatabaseMetadata metadata = new DatabaseMetadata(con,
650: dialect);
651: String[] sql = configuration
652: .generateSchemaUpdateScript(dialect, metadata);
653: executeSchemaScript(con, sql);
654: return null;
655: }
656: });
657: }
658:
659: /**
660: * Execute the given schema script on the given JDBC Connection.
661: * <p>Note that the default implementation will log unsuccessful statements
662: * and continue to execute. Override the <code>executeSchemaStatement</code>
663: * method to treat failures differently.
664: * @param con the JDBC Connection to execute the script on
665: * @param sql the SQL statements to execute
666: * @throws SQLException if thrown by JDBC methods
667: * @see #executeSchemaStatement
668: */
669: protected void executeSchemaScript(Connection con, String[] sql)
670: throws SQLException {
671: if (sql != null && sql.length > 0) {
672: boolean oldAutoCommit = con.getAutoCommit();
673: if (!oldAutoCommit) {
674: con.setAutoCommit(true);
675: }
676: try {
677: Statement stmt = con.createStatement();
678: try {
679: for (int i = 0; i < sql.length; i++) {
680: executeSchemaStatement(stmt, sql[i]);
681: }
682: } finally {
683: JdbcUtils.closeStatement(stmt);
684: }
685: } finally {
686: if (!oldAutoCommit) {
687: con.setAutoCommit(false);
688: }
689: }
690: }
691: }
692:
693: /**
694: * Execute the given schema SQL on the given JDBC Statement.
695: * <p>Note that the default implementation will log unsuccessful statements
696: * and continue to execute. Override this method to treat failures differently.
697: * @param stmt the JDBC Statement to execute the SQL on
698: * @param sql the SQL statement to execute
699: * @throws SQLException if thrown by JDBC methods (and considered fatal)
700: */
701: protected void executeSchemaStatement(Statement stmt, String sql)
702: throws SQLException {
703: if (logger.isDebugEnabled()) {
704: logger.debug("Executing schema statement: " + sql);
705: }
706: try {
707: stmt.executeUpdate(sql);
708: } catch (SQLException ex) {
709: if (logger.isWarnEnabled()) {
710: logger
711: .warn("Unsuccessful schema statement: " + sql,
712: ex);
713: }
714: }
715: }
716:
717: /**
718: * Return the Configuration object used to build the SessionFactory.
719: * Allows access to configuration metadata stored there (rarely needed).
720: */
721: public Configuration getConfiguration() {
722: return configuration;
723: }
724:
725: /**
726: * Return the singleton SessionFactory.
727: */
728: public Object getObject() {
729: return this .sessionFactory;
730: }
731:
732: public Class getObjectType() {
733: return (this .sessionFactory != null) ? this .sessionFactory
734: .getClass() : SessionFactory.class;
735: }
736:
737: public boolean isSingleton() {
738: return true;
739: }
740:
741: /**
742: * Close the SessionFactory on bean factory shutdown.
743: */
744: public void destroy() throws HibernateException {
745: logger.info("Closing Hibernate SessionFactory");
746: if (this .dataSource != null) {
747: // Make given DataSource available for potential SchemaExport,
748: // which unfortunately reinstantiates a ConnectionProvider.
749: configTimeDataSourceHolder.set(this .dataSource);
750: }
751: try {
752: this .sessionFactory.close();
753: } finally {
754: if (this .dataSource != null) {
755: // Reset DataSource holder.
756: configTimeDataSourceHolder.set(null);
757: }
758: }
759: }
760:
761: }
|