1 16 17 package org.springframework.orm.hibernate; 18 19 import java.sql.Connection ; 20 21 import javax.sql.DataSource ; 22 23 import net.sf.hibernate.FlushMode; 24 import net.sf.hibernate.HibernateException; 25 import net.sf.hibernate.Interceptor; 26 import net.sf.hibernate.JDBCException; 27 import net.sf.hibernate.Session; 28 import net.sf.hibernate.SessionFactory; 29 30 import org.springframework.beans.BeansException; 31 import org.springframework.beans.factory.BeanFactory; 32 import org.springframework.beans.factory.BeanFactoryAware; 33 import org.springframework.beans.factory.InitializingBean; 34 import org.springframework.dao.DataAccessException; 35 import org.springframework.jdbc.datasource.ConnectionHolder; 36 import org.springframework.jdbc.datasource.DataSourceUtils; 37 import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport; 38 import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; 39 import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; 40 import org.springframework.jdbc.support.SQLExceptionTranslator; 41 import org.springframework.transaction.CannotCreateTransactionException; 42 import org.springframework.transaction.IllegalTransactionStateException; 43 import org.springframework.transaction.TransactionDefinition; 44 import org.springframework.transaction.TransactionSystemException; 45 import org.springframework.transaction.support.AbstractPlatformTransactionManager; 46 import org.springframework.transaction.support.DefaultTransactionStatus; 47 import org.springframework.transaction.support.ResourceTransactionManager; 48 import org.springframework.transaction.support.TransactionSynchronizationManager; 49 50 130 public class HibernateTransactionManager extends AbstractPlatformTransactionManager 131 implements ResourceTransactionManager, BeanFactoryAware, InitializingBean { 132 133 private SessionFactory sessionFactory; 134 135 private DataSource dataSource; 136 137 private boolean autodetectDataSource = true; 138 139 private Object entityInterceptor; 140 141 private SQLExceptionTranslator jdbcExceptionTranslator; 142 143 147 private BeanFactory beanFactory; 148 149 150 155 public HibernateTransactionManager() { 156 } 157 158 162 public HibernateTransactionManager(SessionFactory sessionFactory) { 163 this.sessionFactory = sessionFactory; 164 afterPropertiesSet(); 165 } 166 167 170 public void setSessionFactory(SessionFactory sessionFactory) { 171 this.sessionFactory = sessionFactory; 172 } 173 174 177 public SessionFactory getSessionFactory() { 178 return this.sessionFactory; 179 } 180 181 205 public void setDataSource(DataSource dataSource) { 206 if (dataSource instanceof TransactionAwareDataSourceProxy) { 207 this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); 211 } 212 else { 213 this.dataSource = dataSource; 214 } 215 } 216 217 220 public DataSource getDataSource() { 221 return this.dataSource; 222 } 223 224 232 public void setAutodetectDataSource(boolean autodetectDataSource) { 233 this.autodetectDataSource = autodetectDataSource; 234 } 235 236 250 public void setEntityInterceptorBeanName(String entityInterceptorBeanName) { 251 this.entityInterceptor = entityInterceptorBeanName; 252 } 253 254 267 public void setEntityInterceptor(Interceptor entityInterceptor) { 268 this.entityInterceptor = entityInterceptor; 269 } 270 271 281 public Interceptor getEntityInterceptor() throws IllegalStateException , BeansException { 282 if (this.entityInterceptor instanceof Interceptor) { 283 return (Interceptor) entityInterceptor; 284 } 285 else if (this.entityInterceptor instanceof String ) { 286 if (this.beanFactory == null) { 287 throw new IllegalStateException ("Cannot get entity interceptor via bean name if no bean factory set"); 288 } 289 String beanName = (String ) this.entityInterceptor; 290 return (Interceptor) this.beanFactory.getBean(beanName, Interceptor.class); 291 } 292 else { 293 return null; 294 } 295 } 296 297 310 public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) { 311 this.jdbcExceptionTranslator = jdbcExceptionTranslator; 312 } 313 314 320 public synchronized SQLExceptionTranslator getJdbcExceptionTranslator() { 321 if (this.jdbcExceptionTranslator == null) { 322 if (getDataSource() != null) { 323 this.jdbcExceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(getDataSource()); 324 } 325 else { 326 this.jdbcExceptionTranslator = SessionFactoryUtils.newJdbcExceptionTranslator(getSessionFactory()); 327 } 328 } 329 return this.jdbcExceptionTranslator; 330 } 331 332 337 public void setBeanFactory(BeanFactory beanFactory) { 338 this.beanFactory = beanFactory; 339 } 340 341 public void afterPropertiesSet() { 342 if (getSessionFactory() == null) { 343 throw new IllegalArgumentException ("Property 'sessionFactory' is required"); 344 } 345 if (this.entityInterceptor instanceof String && this.beanFactory == null) { 346 throw new IllegalArgumentException ("'beanFactory' is required for 'entityInterceptorBeanName'"); 347 } 348 349 if (this.autodetectDataSource && getDataSource() == null) { 351 DataSource sfds = SessionFactoryUtils.getDataSource(getSessionFactory()); 352 if (sfds != null) { 353 if (logger.isInfoEnabled()) { 355 logger.info("Using DataSource [" + sfds + 356 "] of Hibernate SessionFactory for HibernateTransactionManager"); 357 } 358 setDataSource(sfds); 359 } 360 } 361 } 362 363 364 public Object getResourceFactory() { 365 return getSessionFactory(); 366 } 367 368 protected Object doGetTransaction() { 369 HibernateTransactionObject txObject = new HibernateTransactionObject(); 370 txObject.setSavepointAllowed(isNestedTransactionAllowed()); 371 372 SessionHolder sessionHolder = 373 (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory()); 374 if (sessionHolder != null) { 375 if (logger.isDebugEnabled()) { 376 logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + 377 "] for Hibernate transaction"); 378 } 379 txObject.setSessionHolder(sessionHolder, false); 380 } 381 382 if (getDataSource() != null) { 383 ConnectionHolder conHolder = (ConnectionHolder) 384 TransactionSynchronizationManager.getResource(getDataSource()); 385 txObject.setConnectionHolder(conHolder); 386 } 387 388 return txObject; 389 } 390 391 protected boolean isExistingTransaction(Object transaction) { 392 return ((HibernateTransactionObject) transaction).hasTransaction(); 393 } 394 395 protected void doBegin(Object transaction, TransactionDefinition definition) { 396 HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; 397 398 if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) { 399 throw new IllegalTransactionStateException( 400 "Pre-bound JDBC Connection found! HibernateTransactionManager does not support " + 401 "running within DataSourceTransactionManager if told to manage the DataSource itself. " + 402 "It is recommended to use a single HibernateTransactionManager for all transactions " + 403 "on a single DataSource, no matter whether Hibernate or JDBC access."); 404 } 405 406 Session session = null; 407 408 try { 409 if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) { 410 Interceptor entityInterceptor = getEntityInterceptor(); 411 Session newSession = (entityInterceptor != null ? 412 getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession()); 413 if (logger.isDebugEnabled()) { 414 logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction"); 415 } 416 txObject.setSessionHolder(new SessionHolder(newSession), true); 417 } 418 419 txObject.getSessionHolder().setSynchronizedWithTransaction(true); 420 session = txObject.getSessionHolder().getSession(); 421 422 Connection con = session.connection(); 423 Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); 424 txObject.setPreviousIsolationLevel(previousIsolationLevel); 425 426 if (definition.isReadOnly() && txObject.isNewSessionHolder()) { 427 session.setFlushMode(FlushMode.NEVER); 429 } 430 431 if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) { 432 FlushMode flushMode = session.getFlushMode(); 434 if (FlushMode.NEVER.equals(flushMode)) { 435 session.setFlushMode(FlushMode.AUTO); 436 txObject.getSessionHolder().setPreviousFlushMode(flushMode); 437 } 438 } 439 440 txObject.getSessionHolder().setTransaction(session.beginTransaction()); 442 443 int timeout = determineTimeout(definition); 445 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 446 txObject.getSessionHolder().setTimeoutInSeconds(timeout); 447 } 448 449 if (getDataSource() != null) { 451 ConnectionHolder conHolder = new ConnectionHolder(con); 452 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 453 conHolder.setTimeoutInSeconds(timeout); 454 } 455 if (logger.isDebugEnabled()) { 456 logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]"); 457 } 458 TransactionSynchronizationManager.bindResource(getDataSource(), conHolder); 459 txObject.setConnectionHolder(conHolder); 460 } 461 462 if (txObject.isNewSessionHolder()) { 464 TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder()); 465 } 466 } 467 468 catch (Exception ex) { 469 SessionFactoryUtils.closeSession(session); 470 throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex); 471 } 472 } 473 474 protected Object doSuspend(Object transaction) { 475 HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; 476 txObject.setSessionHolder(null, false); 477 SessionHolder sessionHolder = 478 (SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory()); 479 txObject.setConnectionHolder(null); 480 ConnectionHolder connectionHolder = null; 481 if (getDataSource() != null) { 482 connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource()); 483 } 484 return new SuspendedResourcesHolder(sessionHolder, connectionHolder); 485 } 486 487 protected void doResume(Object transaction, Object suspendedResources) { 488 SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources; 489 if (TransactionSynchronizationManager.hasResource(getSessionFactory())) { 490 TransactionSynchronizationManager.unbindResource(getSessionFactory()); 493 } 494 TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder()); 495 if (getDataSource() != null) { 496 TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder()); 497 } 498 } 499 500 protected void doCommit(DefaultTransactionStatus status) { 501 HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction(); 502 if (status.isDebug()) { 503 logger.debug("Committing Hibernate transaction on Session [" + 504 txObject.getSessionHolder().getSession() + "]"); 505 } 506 try { 507 txObject.getSessionHolder().getTransaction().commit(); 508 } 509 catch (net.sf.hibernate.TransactionException ex) { 510 throw new TransactionSystemException("Could not commit Hibernate transaction", ex); 512 } 513 catch (HibernateException ex) { 514 throw convertHibernateAccessException(ex); 516 } 517 } 518 519 protected void doRollback(DefaultTransactionStatus status) { 520 HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction(); 521 if (status.isDebug()) { 522 logger.debug("Rolling back Hibernate transaction on Session [" + 523 txObject.getSessionHolder().getSession() + "]"); 524 } 525 try { 526 txObject.getSessionHolder().getTransaction().rollback(); 527 } 528 catch (net.sf.hibernate.TransactionException ex) { 529 throw new TransactionSystemException("Could not roll back Hibernate transaction", ex); 530 } 531 catch (HibernateException ex) { 532 throw convertHibernateAccessException(ex); 534 } 535 finally { 536 if (!txObject.isNewSessionHolder()) { 537 txObject.getSessionHolder().getSession().clear(); 540 } 541 } 542 } 543 544 protected void doSetRollbackOnly(DefaultTransactionStatus status) { 545 HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction(); 546 if (status.isDebug()) { 547 logger.debug("Setting Hibernate transaction on Session [" + 548 txObject.getSessionHolder().getSession() + "] rollback-only"); 549 } 550 txObject.setRollbackOnly(); 551 } 552 553 protected void doCleanupAfterCompletion(Object transaction) { 554 HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; 555 556 if (txObject.isNewSessionHolder()) { 558 TransactionSynchronizationManager.unbindResource(getSessionFactory()); 559 } 560 561 if (getDataSource() != null) { 563 TransactionSynchronizationManager.unbindResource(getDataSource()); 564 } 565 566 try { 567 Connection con = txObject.getSessionHolder().getSession().connection(); 568 DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel()); 569 } 570 catch (HibernateException ex) { 571 logger.info("Could not access JDBC Connection of Hibernate Session", ex); 572 } 573 574 Session session = txObject.getSessionHolder().getSession(); 575 if (txObject.isNewSessionHolder()) { 576 if (logger.isDebugEnabled()) { 577 logger.debug("Closing Hibernate Session [" + session + "] after transaction"); 578 } 579 SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory()); 580 } 581 else { 582 if (logger.isDebugEnabled()) { 583 logger.debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction"); 584 } 585 if (txObject.getSessionHolder().getPreviousFlushMode() != null) { 586 session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode()); 587 } 588 } 589 txObject.getSessionHolder().clear(); 590 } 591 592 593 600 protected DataAccessException convertHibernateAccessException(HibernateException ex) { 601 if (ex instanceof JDBCException) { 602 return convertJdbcAccessException((JDBCException) ex); 603 } 604 return SessionFactoryUtils.convertHibernateAccessException(ex); 605 } 606 607 615 protected DataAccessException convertJdbcAccessException(JDBCException ex) { 616 return getJdbcExceptionTranslator().translate( 617 "Hibernate operation: " + ex.getMessage(), null, ex.getSQLException()); 618 } 619 620 621 630 private static class HibernateTransactionObject extends JdbcTransactionObjectSupport { 631 632 private SessionHolder sessionHolder; 633 634 private boolean newSessionHolder; 635 636 public void setSessionHolder(SessionHolder sessionHolder, boolean newSessionHolder) { 637 this.sessionHolder = sessionHolder; 638 this.newSessionHolder = newSessionHolder; 639 } 640 641 public SessionHolder getSessionHolder() { 642 return this.sessionHolder; 643 } 644 645 public boolean isNewSessionHolder() { 646 return this.newSessionHolder; 647 } 648 649 public boolean hasTransaction() { 650 return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null); 651 } 652 653 public void setRollbackOnly() { 654 getSessionHolder().setRollbackOnly(); 655 if (hasConnectionHolder()) { 656 getConnectionHolder().setRollbackOnly(); 657 } 658 } 659 660 public boolean isRollbackOnly() { 661 return getSessionHolder().isRollbackOnly() || 662 (hasConnectionHolder() && getConnectionHolder().isRollbackOnly()); 663 } 664 } 665 666 667 671 private static class SuspendedResourcesHolder { 672 673 private final SessionHolder sessionHolder; 674 675 private final ConnectionHolder connectionHolder; 676 677 private SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder) { 678 this.sessionHolder = sessionHolder; 679 this.connectionHolder = conHolder; 680 } 681 682 private SessionHolder getSessionHolder() { 683 return this.sessionHolder; 684 } 685 686 private ConnectionHolder getConnectionHolder() { 687 return this.connectionHolder; 688 } 689 } 690 691 } 692 | Popular Tags |