Spring Framework
  1. Spring Framework
  2. SPR-9020

Hibernate4 version of SpringSessionContext.currentSession() does not create a session if TransactionSynchronizationManager does not contain one

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Major Major
    • Resolution: Complete
    • Affects Version/s: 3.1 GA
    • Fix Version/s: 4.1 RC2
    • Component/s: Core
    • Labels:
    • Last commented by a User:
      false

      Description

      The Hibernate4 support of Spring 3.1 does not open and register a session in case of the method called is annotated with @Transactional(propagation = Propagation.SUPPORTS).
      In this case HibernateTransactionManager.doBegin() is never called which is the only place where hibernate session is opened. The result is, that all read operations which do not require a transaction will fail because the call to SessionFactory.currentSession() will result in an exception.

      The Hibernate3 implementation contains a fallback for this case in SessionFactoryUtils.doGetSession(...) which is missing in the corresponding Hibernate4 implementation of SpringSessionContext.currentSession().

      If the transaction propagation is changed to REQUIRES_NEW everything is working fine.

        Issue Links

          Activity

          Hide
          thiago andrade added a comment -

          For when the spring developers are scheduling to fix this bug?

          Show
          thiago andrade added a comment - For when the spring developers are scheduling to fix this bug?
          Hide
          Juergen Hoeller added a comment -

          Note that the effort of managing a Spring transaction scope within the application - just for the purpose of a read operation - is negligible compared to the amount of CPU cycles wasted by creating a new Hibernate Session itself... Admittedly this can become a bit more concerning with JTA in an application server environment, where the application server's JTA subsystem is also wasting some cycles on begin and commit. However, is that really significant? Have you tested that overhead or are you simply assuming that it's concerning?

          In addition, even our old Hibernate 3 behavior of creating a locally synchronized Session within a SUPPORTS transaction involves the management of a local transaction scope. The only difference with using REQUIRED is that the JTA subsystem and/or the underlying database resource is also being told about the transaction scope and can consider it accordingly. In a well-optimized scenario, the latter effect can be positive in terms of optimized resource management within that transaction scope.

          It's also still true that the Hibernate team has a very strong opinion on this: They want you to execute all Hibernate operations within a transaction, and - with every Hibernate release - make it harder and harder to achieve 100% correct behavior outside of a transaction, for both users and framework integrators. They simply don't consider that a first-class scenario and don't expose proper hooks etc (see JTASessionContext and its exception design for an example).

          Finally, if this is really commonly desired among Spring users even at this point, we can revisit this for Spring Framework 4.1. We definitely won't do custom JTA-based synchronization (that's pointless since the JTA synchronization facility will only work within active JTA transactions, and that's covered by Hibernate's JTASessionContext already) but can at least consider a best-effort Spring-based synchronization arrangement as a fallback if none of the regular Session retrieval strategies worked out.

          Juergen

          Show
          Juergen Hoeller added a comment - Note that the effort of managing a Spring transaction scope within the application - just for the purpose of a read operation - is negligible compared to the amount of CPU cycles wasted by creating a new Hibernate Session itself... Admittedly this can become a bit more concerning with JTA in an application server environment, where the application server's JTA subsystem is also wasting some cycles on begin and commit. However, is that really significant? Have you tested that overhead or are you simply assuming that it's concerning? In addition, even our old Hibernate 3 behavior of creating a locally synchronized Session within a SUPPORTS transaction involves the management of a local transaction scope. The only difference with using REQUIRED is that the JTA subsystem and/or the underlying database resource is also being told about the transaction scope and can consider it accordingly. In a well-optimized scenario, the latter effect can be positive in terms of optimized resource management within that transaction scope. It's also still true that the Hibernate team has a very strong opinion on this: They want you to execute all Hibernate operations within a transaction, and - with every Hibernate release - make it harder and harder to achieve 100% correct behavior outside of a transaction, for both users and framework integrators. They simply don't consider that a first-class scenario and don't expose proper hooks etc (see JTASessionContext and its exception design for an example). Finally, if this is really commonly desired among Spring users even at this point, we can revisit this for Spring Framework 4.1. We definitely won't do custom JTA-based synchronization (that's pointless since the JTA synchronization facility will only work within active JTA transactions, and that's covered by Hibernate's JTASessionContext already) but can at least consider a best-effort Spring-based synchronization arrangement as a fallback if none of the regular Session retrieval strategies worked out. Juergen
          Hide
          Bilal Ahmed added a comment - - edited

          Hi All,

          I am migrating my application from Spring 3.0.5.RELEASE to 4.3.5.Final, and Hibernate 3.6.0.Beta2 to 4.3.5.Final.
          Primary reason for this upgrade is to support multi-tenancy in our application using Hibernate 4.

          Application has been working perfectly fine in older versions of Spring and Hibernate. To Juergen's point, of using "REQUIRED" instead of "SUPPORTS",
          I am not marking my method by any Propagation strategy, hence by default it is "REQUIRED", according to Spring documentation ... but ... still getting this error. I even tried to explicitly mark my transactional method with "REQUIRED" .. no luck.

          Need to know, what's the fate of this bug, as it is becoming an obstacle for us at this point.

          Here are some details :

          Transactional Method Added in Service Class - From Spring Log
          -----------------------------------------------------------------------------------
          [2014-06-25 15:00:33] [DEBUG ][org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(AbstractFallbackTransactionAttributeSource.java:108)] "Adding transactional method 'UserServiceImpl.validateUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''"

          Service Layer
          ------------------
          @Service("userServiceBean")
          public class UserServiceImpl implements UserService{

          private static final Logger logger = MyVacationLogger.getLogger(UserServiceImpl.class);

          @Autowired(required=true)
          private UserDAO userDAO;

          @Transactional(readOnly=true, propagation = Propagation.REQUIRED)
          public UserBean validateUser(UserBean userbean) throws MyVacationRuntimeException

          { .... }

          }

          DAO Layer
          --------------
          @Repository("UserDAOBean")
          public class UserDAOImpl implements UserDAO {

          @Autowired(required=true)
          @Qualifier("commondbSessionFactory")
          private SessionFactory sessFactory;

          @Override
          public UserDTO findUserbyEmail(String email) throws DataAccessException

          { Session sess = sessFactory.getCurrentSession(); << Error Line .... }

          }

          Error
          --------
          Caused by: org.hibernate.HibernateException: No Session found for current thread
          at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106)
          at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
          at com.ellisdon.portal.mv.dao.impl.UserDAOImpl.findUserbyEmail(UserDAOImpl.java:31)
          at com.ellisdon.portal.mv.service.impl.UserServiceImpl.validateUser(UserServiceImpl.java:38)

          Show
          Bilal Ahmed added a comment - - edited Hi All, I am migrating my application from Spring 3.0.5.RELEASE to 4.3.5.Final, and Hibernate 3.6.0.Beta2 to 4.3.5.Final. Primary reason for this upgrade is to support multi-tenancy in our application using Hibernate 4. Application has been working perfectly fine in older versions of Spring and Hibernate. To Juergen's point, of using "REQUIRED" instead of "SUPPORTS", I am not marking my method by any Propagation strategy, hence by default it is "REQUIRED", according to Spring documentation ... but ... still getting this error. I even tried to explicitly mark my transactional method with "REQUIRED" .. no luck. Need to know, what's the fate of this bug, as it is becoming an obstacle for us at this point. Here are some details : Transactional Method Added in Service Class - From Spring Log ----------------------------------------------------------------------------------- [2014-06-25 15:00:33] [DEBUG ] [org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(AbstractFallbackTransactionAttributeSource.java:108)] "Adding transactional method 'UserServiceImpl.validateUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''" Service Layer ------------------ @Service("userServiceBean") public class UserServiceImpl implements UserService{ private static final Logger logger = MyVacationLogger.getLogger(UserServiceImpl.class); @Autowired(required=true) private UserDAO userDAO; @Transactional(readOnly=true, propagation = Propagation.REQUIRED) public UserBean validateUser(UserBean userbean) throws MyVacationRuntimeException { .... } } DAO Layer -------------- @Repository("UserDAOBean") public class UserDAOImpl implements UserDAO { @Autowired(required=true) @Qualifier("commondbSessionFactory") private SessionFactory sessFactory; @Override public UserDTO findUserbyEmail(String email) throws DataAccessException { Session sess = sessFactory.getCurrentSession(); << Error Line .... } } Error -------- Caused by: org.hibernate.HibernateException: No Session found for current thread at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106) at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014) at com.ellisdon.portal.mv.dao.impl.UserDAOImpl.findUserbyEmail(UserDAOImpl.java:31) at com.ellisdon.portal.mv.service.impl.UserServiceImpl.validateUser(UserServiceImpl.java:38)
          Hide
          Luke Maurer added a comment -

          It appears that programmatic demarcation using TransactionTemplate suffers from this same issue.

          Is it fair to say that Spring simply does not support Propagation.SUPPORTS with Hibernate 4? I fail to see what use SUPPORTS has if it doesn't bind a Hibernate session to the thread. By my understanding, if there's no transaction when a SUPPORTS method is called, the code runs non-transactionally but with a single Hibernate session used for all accesses. If SessionFactory.getCurrentSession() isn't the means to get at that single session, what is? Or, if there's no session bound to the thread at all in this case, how does @Transactional(propagation=SUPPORTS) differ from no annotation at all?

          Show
          Luke Maurer added a comment - It appears that programmatic demarcation using TransactionTemplate suffers from this same issue. Is it fair to say that Spring simply does not support Propagation.SUPPORTS with Hibernate 4? I fail to see what use SUPPORTS has if it doesn't bind a Hibernate session to the thread. By my understanding, if there's no transaction when a SUPPORTS method is called, the code runs non-transactionally but with a single Hibernate session used for all accesses. If SessionFactory.getCurrentSession() isn't the means to get at that single session, what is? Or, if there's no session bound to the thread at all in this case, how does @Transactional(propagation=SUPPORTS) differ from no annotation at all?
          Hide
          Juergen Hoeller added a comment -

          Added for 4.1 RC2 now - please give it a try in the upcoming 4.1 snapshot, or in 4.1 RC2 itself once available!

          We only delegate to Hibernate's JTASessionContext in case of an active JTA transaction now, manually checking the JTA status upfront. If no JTA transaction is available, we lazily create and bind a Hibernate Session to the current Spring transaction synchronization scope (for propagation SUPPORTS).

          Note that in contrast to our Hibernate 3 support, we don't check for any interleaving with JTA transactions here. This propagation SUPPORTS mode is only meant to work within a HibernateTransactionManager arrangement, and generally only meant to be used for transaction scopes which won't be upgraded to a full REQUIRES transaction further down the call stack.

          Juergen

          Show
          Juergen Hoeller added a comment - Added for 4.1 RC2 now - please give it a try in the upcoming 4.1 snapshot, or in 4.1 RC2 itself once available! We only delegate to Hibernate's JTASessionContext in case of an active JTA transaction now, manually checking the JTA status upfront. If no JTA transaction is available, we lazily create and bind a Hibernate Session to the current Spring transaction synchronization scope (for propagation SUPPORTS). Note that in contrast to our Hibernate 3 support, we don't check for any interleaving with JTA transactions here. This propagation SUPPORTS mode is only meant to work within a HibernateTransactionManager arrangement, and generally only meant to be used for transaction scopes which won't be upgraded to a full REQUIRES transaction further down the call stack. Juergen

            People

            • Assignee:
              Juergen Hoeller
              Reporter:
              Reto Urfer
              Last updater:
              Juergen Hoeller
            • Votes:
              19 Vote for this issue
              Watchers:
              28 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                36 weeks ago