1 16 17 package org.springframework.orm.hibernate3; 18 19 import java.sql.Connection ; 20 21 import javax.sql.DataSource ; 22 23 import org.hibernate.ConnectionReleaseMode; 24 import org.hibernate.FlushMode; 25 import org.hibernate.HibernateException; 26 import org.hibernate.Interceptor; 27 import org.hibernate.JDBCException; 28 import org.hibernate.Session; 29 import org.hibernate.SessionFactory; 30 import org.hibernate.Transaction; 31 import org.hibernate.exception.GenericJDBCException; 32 import org.hibernate.impl.SessionImpl; 33 34 import org.springframework.beans.BeansException; 35 import org.springframework.beans.factory.BeanFactory; 36 import org.springframework.beans.factory.BeanFactoryAware; 37 import org.springframework.beans.factory.InitializingBean; 38 import org.springframework.dao.DataAccessException; 39 import org.springframework.jdbc.datasource.ConnectionHolder; 40 import org.springframework.jdbc.datasource.DataSourceUtils; 41 import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport; 42 import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; 43 import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; 44 import org.springframework.jdbc.support.SQLExceptionTranslator; 45 import org.springframework.transaction.CannotCreateTransactionException; 46 import org.springframework.transaction.IllegalTransactionStateException; 47 import org.springframework.transaction.InvalidIsolationLevelException; 48 import org.springframework.transaction.TransactionDefinition; 49 import org.springframework.transaction.TransactionSystemException; 50 import org.springframework.transaction.support.AbstractPlatformTransactionManager; 51 import org.springframework.transaction.support.DefaultTransactionStatus; 52 import org.springframework.transaction.support.TransactionSynchronizationManager; 53 import org.springframework.transaction.support.ResourceTransactionManager; 54 import org.springframework.util.ClassUtils; 55 56 138 public class HibernateTransactionManager extends AbstractPlatformTransactionManager 139 implements ResourceTransactionManager, BeanFactoryAware, InitializingBean { 140 141 private final static boolean hibernateSetTimeoutAvailable = 144 ClassUtils.hasMethod(Transaction.class, "setTimeout", new Class [] {int.class}); 145 146 147 private SessionFactory sessionFactory; 148 149 private DataSource dataSource; 150 151 private boolean autodetectDataSource = true; 152 153 private boolean prepareConnection = true; 154 155 private Object entityInterceptor; 156 157 private SQLExceptionTranslator jdbcExceptionTranslator; 158 159 private SQLExceptionTranslator defaultJdbcExceptionTranslator; 160 161 165 private BeanFactory beanFactory; 166 167 168 173 public HibernateTransactionManager() { 174 } 175 176 180 public HibernateTransactionManager(SessionFactory sessionFactory) { 181 this.sessionFactory = sessionFactory; 182 afterPropertiesSet(); 183 } 184 185 188 public void setSessionFactory(SessionFactory sessionFactory) { 189 this.sessionFactory = sessionFactory; 190 } 191 192 195 public SessionFactory getSessionFactory() { 196 return this.sessionFactory; 197 } 198 199 223 public void setDataSource(DataSource dataSource) { 224 if (dataSource instanceof TransactionAwareDataSourceProxy) { 225 this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); 229 } 230 else { 231 this.dataSource = dataSource; 232 } 233 } 234 235 238 public DataSource getDataSource() { 239 return this.dataSource; 240 } 241 242 250 public void setAutodetectDataSource(boolean autodetectDataSource) { 251 this.autodetectDataSource = autodetectDataSource; 252 } 253 254 272 public void setPrepareConnection(boolean prepareConnection) { 273 this.prepareConnection = prepareConnection; 274 } 275 276 290 public void setEntityInterceptorBeanName(String entityInterceptorBeanName) { 291 this.entityInterceptor = entityInterceptorBeanName; 292 } 293 294 307 public void setEntityInterceptor(Interceptor entityInterceptor) { 308 this.entityInterceptor = entityInterceptor; 309 } 310 311 321 public Interceptor getEntityInterceptor() throws IllegalStateException , BeansException { 322 if (this.entityInterceptor instanceof Interceptor) { 323 return (Interceptor) entityInterceptor; 324 } 325 else if (this.entityInterceptor instanceof String ) { 326 if (this.beanFactory == null) { 327 throw new IllegalStateException ("Cannot get entity interceptor via bean name if no bean factory set"); 328 } 329 String beanName = (String ) this.entityInterceptor; 330 return (Interceptor) this.beanFactory.getBean(beanName, Interceptor.class); 331 } 332 else { 333 return null; 334 } 335 } 336 337 348 public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) { 349 this.jdbcExceptionTranslator = jdbcExceptionTranslator; 350 } 351 352 355 public SQLExceptionTranslator getJdbcExceptionTranslator() { 356 return this.jdbcExceptionTranslator; 357 } 358 359 364 public void setBeanFactory(BeanFactory beanFactory) { 365 this.beanFactory = beanFactory; 366 } 367 368 public void afterPropertiesSet() { 369 if (getSessionFactory() == null) { 370 throw new IllegalArgumentException ("Property 'sessionFactory' is required"); 371 } 372 if (this.entityInterceptor instanceof String && this.beanFactory == null) { 373 throw new IllegalArgumentException ("Property 'beanFactory' is required for 'entityInterceptorBeanName'"); 374 } 375 376 if (this.autodetectDataSource && getDataSource() == null) { 378 DataSource sfds = SessionFactoryUtils.getDataSource(getSessionFactory()); 379 if (sfds != null) { 380 if (logger.isInfoEnabled()) { 382 logger.info("Using DataSource [" + sfds + 383 "] of Hibernate SessionFactory for HibernateTransactionManager"); 384 } 385 setDataSource(sfds); 386 } 387 } 388 } 389 390 391 public Object getResourceFactory() { 392 return getSessionFactory(); 393 } 394 395 protected Object doGetTransaction() { 396 HibernateTransactionObject txObject = new HibernateTransactionObject(); 397 txObject.setSavepointAllowed(isNestedTransactionAllowed()); 398 399 SessionHolder sessionHolder = 400 (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory()); 401 if (sessionHolder != null) { 402 if (logger.isDebugEnabled()) { 403 logger.debug("Found thread-bound Session [" + 404 SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction"); 405 } 406 txObject.setSessionHolder(sessionHolder, false); 407 } 408 409 if (getDataSource() != null) { 410 ConnectionHolder conHolder = (ConnectionHolder) 411 TransactionSynchronizationManager.getResource(getDataSource()); 412 txObject.setConnectionHolder(conHolder); 413 } 414 415 return txObject; 416 } 417 418 protected boolean isExistingTransaction(Object transaction) { 419 return ((HibernateTransactionObject) transaction).hasTransaction(); 420 } 421 422 protected void doBegin(Object transaction, TransactionDefinition definition) { 423 HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; 424 425 if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) { 426 throw new IllegalTransactionStateException( 427 "Pre-bound JDBC Connection found! HibernateTransactionManager does not support " + 428 "running within DataSourceTransactionManager if told to manage the DataSource itself. " + 429 "It is recommended to use a single HibernateTransactionManager for all transactions " + 430 "on a single DataSource, no matter whether Hibernate or JDBC access."); 431 } 432 433 Session session = null; 434 435 try { 436 if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) { 437 Interceptor entityInterceptor = getEntityInterceptor(); 438 Session newSession = (entityInterceptor != null ? 439 getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession()); 440 if (logger.isDebugEnabled()) { 441 logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) + 442 "] for Hibernate transaction"); 443 } 444 txObject.setSessionHolder(new SessionHolder(newSession), true); 445 } 446 447 txObject.getSessionHolder().setSynchronizedWithTransaction(true); 448 session = txObject.getSessionHolder().getSession(); 449 450 if (this.prepareConnection && isSameConnectionForEntireSession(session)) { 451 if (logger.isDebugEnabled()) { 453 logger.debug( 454 "Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]"); 455 } 456 Connection con = session.connection(); 457 Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); 458 txObject.setPreviousIsolationLevel(previousIsolationLevel); 459 } 460 else { 461 if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { 463 throw new InvalidIsolationLevelException( 465 "HibernateTransactionManager is not allowed to support custom isolation levels: " + 466 "make sure that its 'prepareConnection' flag is on (the default) and that the " + 467 "Hibernate connection release mode is set to 'on_close' (LocalSessionFactoryBean's default)"); 468 } 469 if (logger.isDebugEnabled()) { 470 logger.debug( 471 "Not preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]"); 472 } 473 } 474 475 if (definition.isReadOnly() && txObject.isNewSessionHolder()) { 476 session.setFlushMode(FlushMode.NEVER); 478 } 479 480 if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) { 481 FlushMode flushMode = session.getFlushMode(); 483 if (flushMode.lessThan(FlushMode.COMMIT)) { 484 session.setFlushMode(FlushMode.AUTO); 485 txObject.getSessionHolder().setPreviousFlushMode(flushMode); 486 } 487 } 488 489 Transaction hibTx = null; 490 491 int timeout = determineTimeout(definition); 493 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 494 if (hibernateSetTimeoutAvailable) { 495 hibTx = session.getTransaction(); 498 hibTx.setTimeout(timeout); 499 hibTx.begin(); 500 } 501 else { 502 hibTx = session.beginTransaction(); 505 txObject.getSessionHolder().setTimeoutInSeconds(timeout); 506 } 507 } 508 else { 509 hibTx = session.beginTransaction(); 511 } 512 513 txObject.getSessionHolder().setTransaction(hibTx); 515 516 if (getDataSource() != null) { 518 Connection con = session.connection(); 519 ConnectionHolder conHolder = new ConnectionHolder(con); 520 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 521 conHolder.setTimeoutInSeconds(timeout); 522 } 523 if (logger.isDebugEnabled()) { 524 logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]"); 525 } 526 TransactionSynchronizationManager.bindResource(getDataSource(), conHolder); 527 txObject.setConnectionHolder(conHolder); 528 } 529 530 if (txObject.isNewSessionHolder()) { 532 TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder()); 533 } 534 } 535 536 catch (Exception ex) { 537 SessionFactoryUtils.closeSession(session); 538 throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex); 539 } 540 } 541 542 protected Object doSuspend(Object transaction) { 543 HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; 544 txObject.setSessionHolder(null, false); 545 SessionHolder sessionHolder = 546 (SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory()); 547 txObject.setConnectionHolder(null); 548 ConnectionHolder connectionHolder = null; 549 if (getDataSource() != null) { 550 connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource()); 551 } 552 return new SuspendedResourcesHolder(sessionHolder, connectionHolder); 553 } 554 555 protected void doResume(Object transaction, Object suspendedResources) { 556 SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources; 557 if (TransactionSynchronizationManager.hasResource(getSessionFactory())) { 558 TransactionSynchronizationManager.unbindResource(getSessionFactory()); 561 } 562 TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder()); 563 if (getDataSource() != null) { 564 TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder()); 565 } 566 } 567 568 protected void doCommit(DefaultTransactionStatus status) { 569 HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction(); 570 if (status.isDebug()) { 571 logger.debug("Committing Hibernate transaction on Session [" + 572 SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]"); 573 } 574 try { 575 txObject.getSessionHolder().getTransaction().commit(); 576 } 577 catch (org.hibernate.TransactionException ex) { 578 throw new TransactionSystemException("Could not commit Hibernate transaction", ex); 580 } 581 catch (HibernateException ex) { 582 throw convertHibernateAccessException(ex); 584 } 585 } 586 587 protected void doRollback(DefaultTransactionStatus status) { 588 HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction(); 589 if (status.isDebug()) { 590 logger.debug("Rolling back Hibernate transaction on Session [" + 591 SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]"); 592 } 593 try { 594 txObject.getSessionHolder().getTransaction().rollback(); 595 } 596 catch (org.hibernate.TransactionException ex) { 597 throw new TransactionSystemException("Could not roll back Hibernate transaction", ex); 598 } 599 catch (HibernateException ex) { 600 throw convertHibernateAccessException(ex); 602 } 603 finally { 604 if (!txObject.isNewSessionHolder()) { 605 txObject.getSessionHolder().getSession().clear(); 608 } 609 } 610 } 611 612 protected void doSetRollbackOnly(DefaultTransactionStatus status) { 613 HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction(); 614 if (status.isDebug()) { 615 logger.debug("Setting Hibernate transaction on Session [" + 616 SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "] rollback-only"); 617 } 618 txObject.setRollbackOnly(); 619 } 620 621 protected void doCleanupAfterCompletion(Object transaction) { 622 HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; 623 624 if (txObject.isNewSessionHolder()) { 626 TransactionSynchronizationManager.unbindResource(getSessionFactory()); 627 } 628 629 if (getDataSource() != null) { 631 TransactionSynchronizationManager.unbindResource(getDataSource()); 632 } 633 634 Session session = txObject.getSessionHolder().getSession(); 635 if (this.prepareConnection && session.isConnected() && isSameConnectionForEntireSession(session)) { 636 try { 640 Connection con = session.connection(); 641 DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel()); 642 } 643 catch (HibernateException ex) { 644 logger.debug("Could not access JDBC Connection of Hibernate Session", ex); 645 } 646 } 647 648 if (txObject.isNewSessionHolder()) { 649 if (logger.isDebugEnabled()) { 650 logger.debug("Closing Hibernate Session [" + SessionFactoryUtils.toString(session) + 651 "] after transaction"); 652 } 653 SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory()); 654 } 655 else { 656 if (logger.isDebugEnabled()) { 657 logger.debug("Not closing pre-bound Hibernate Session [" + 658 SessionFactoryUtils.toString(session) + "] after transaction"); 659 } 660 if (txObject.getSessionHolder().getPreviousFlushMode() != null) { 661 session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode()); 662 } 663 if (hibernateSetTimeoutAvailable) { 664 session.disconnect(); 670 } 671 } 672 txObject.getSessionHolder().clear(); 673 } 674 675 687 protected boolean isSameConnectionForEntireSession(Session session) { 688 if (!(session instanceof SessionImpl)) { 689 return true; 691 } 692 ConnectionReleaseMode releaseMode = ((SessionImpl) session).getConnectionReleaseMode(); 693 return ConnectionReleaseMode.ON_CLOSE.equals(releaseMode); 694 } 695 696 697 707 protected DataAccessException convertHibernateAccessException(HibernateException ex) { 708 if (getJdbcExceptionTranslator() != null && ex instanceof JDBCException) { 709 return convertJdbcAccessException((JDBCException) ex, getJdbcExceptionTranslator()); 710 } 711 else if (GenericJDBCException.class.equals(ex.getClass())) { 712 return convertJdbcAccessException((GenericJDBCException) ex, getDefaultJdbcExceptionTranslator()); 713 } 714 return SessionFactoryUtils.convertHibernateAccessException(ex); 715 } 716 717 725 protected DataAccessException convertJdbcAccessException(JDBCException ex, SQLExceptionTranslator translator) { 726 return translator.translate("Hibernate flushing: " + ex.getMessage(), ex.getSQL(), ex.getSQLException()); 727 } 728 729 735 protected synchronized SQLExceptionTranslator getDefaultJdbcExceptionTranslator() { 736 if (this.defaultJdbcExceptionTranslator == null) { 737 if (getDataSource() != null) { 738 this.defaultJdbcExceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(getDataSource()); 739 } 740 else { 741 this.defaultJdbcExceptionTranslator = SessionFactoryUtils.newJdbcExceptionTranslator(getSessionFactory()); 742 } 743 } 744 return this.defaultJdbcExceptionTranslator; 745 } 746 747 748 757 private static class HibernateTransactionObject extends JdbcTransactionObjectSupport { 758 759 private SessionHolder sessionHolder; 760 761 private boolean newSessionHolder; 762 763 public void setSessionHolder(SessionHolder sessionHolder, boolean newSessionHolder) { 764 this.sessionHolder = sessionHolder; 765 this.newSessionHolder = newSessionHolder; 766 } 767 768 public SessionHolder getSessionHolder() { 769 return this.sessionHolder; 770 } 771 772 public boolean isNewSessionHolder() { 773 return this.newSessionHolder; 774 } 775 776 public boolean hasTransaction() { 777 return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null); 778 } 779 780 public void setRollbackOnly() { 781 getSessionHolder().setRollbackOnly(); 782 if (hasConnectionHolder()) { 783 getConnectionHolder().setRollbackOnly(); 784 } 785 } 786 787 public boolean isRollbackOnly() { 788 return getSessionHolder().isRollbackOnly() || 789 (hasConnectionHolder() && getConnectionHolder().isRollbackOnly()); 790 } 791 } 792 793 794 798 private static class SuspendedResourcesHolder { 799 800 private final SessionHolder sessionHolder; 801 802 private final ConnectionHolder connectionHolder; 803 804 private SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder) { 805 this.sessionHolder = sessionHolder; 806 this.connectionHolder = conHolder; 807 } 808 809 private SessionHolder getSessionHolder() { 810 return this.sessionHolder; 811 } 812 813 private ConnectionHolder getConnectionHolder() { 814 return this.connectionHolder; 815 } 816 } 817 818 } 819 | Popular Tags |