Announcement Announcement Module
Collapse
No announcement yet.
"Nested" transaction within Open Session In View Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • "Nested" transaction within Open Session In View

    Hi all,

    I'm building a web app using J2EE1.4, Spring 1.2.8, Hibernate 3.2.0 and Oracle10g. To access the db I use DAO classes that get a HibernateTemplate injected by the Spring Framework. In the web layer, I use the Spring OpenSessionInViewFilter (OSIVF) to enable accessing persistent objects in the view.

    I configured a Spring exceptionResolver to handle the different types of DataAccessException and show these in a meaningful way to end users. This works fine except for Data Retrieval Exceptions (caused by Hibernate ObjectNotFoundException (ONFE)). Because I use the OSIVF, when retrieving an object by id, the HibernateTemplate.load() method in the DAO only returns a proxy that throws the ONFE at the time of view rendering (which is too late to be able to create a meaningful message to the user).

    I tried to work around this problem in the following ways:
    • Use programmatic transaction demarcation in the DAO code (e.g. tx.begin() and tx.commit() surrounding the HibernateTemplate.load() statement). This solves the problem but creates inconsistency with the rest of the DAO code that uses declarative transaction demarcation by the Spring framework.
    • Use HibernateTemplate.get(). This forces me to create additional code in the Controllers/Servlets checking for null values and throwing custom exceptions in that case. It seems quite obvious to me that reusing the Spring DataAccessException is a better approach.
    • Declarative transaction of the DAO's getById() method. This should be the best solution, but I didn't manage to make this work. Somehow, it seems that whatever propagation rule (e.g. PROPAGATION_NEVER or PROPAGATION_REQUIRES_NEW) I try in the transaction property, is ignored and the transaction does _not_ commit after the getById() method returns. I declared propagation rules in the configuration file as follows:
      Code:
      <bean id="modelDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
      	<property name="transactionManager" ref="txManager" />
      	<property name="target" ref="hibModelDAO" />
      	<property name="transactionAttributes">
      		<props>
      			<prop key="addModel">PROPAGATION_REQUIRED</prop>
      			<prop key="deleteModel">PROPAGATION_REQUIRED</prop>
      			<prop key="updateModel">PROPAGATION_REQUIRED</prop>
      			<prop key="getById">PROPAGATION_NEVER, readOnly</prop>
      		</props>
      	</property>
      </bean>
      where the transaction demarcation for the other DAO methods seem to work perfectly.

    Does anyone have an idea on how to make a "nested" transaction as mentioned above commit on time when using an open session in view filter?

    Thanks in advance

  • #2
    Your current configuration isn't going to work. You specified 'PROPAGATION_NEVER'. Changing it to 'PROPAGATION_REQUIRES_NEW' should work, however it depends on HOW you call the getById method. If you call it from inside the dao it is never going to work, only method calls from outside the dao will be intercepted.

    Use HibernateTemplate.get(). This forces me to create additional code in the Controllers/Servlets checking for null values and throwing custom exceptions in that case. It seems quite obvious to me that reusing the Spring DataAccessException is a better approach.
    You can handle this in your dao, so you need to check in 1 place only. You could create a getRequiredById, if nothing is found throw an exception.

    Comment


    • #3
      Hello Marten,

      thanks for the swift response.

      Originally posted by mdeinum View Post
      Your current configuration isn't going to work. You specified 'PROPAGATION_NEVER'. Changing it to 'PROPAGATION_REQUIRES_NEW' should work, however it depends on HOW you call the getById method. If you call it from inside the dao it is never going to work, only method calls from outside the dao will be intercepted.
      I already tried to use 'PROPAGATION_REQUIRES_NEW', or even the more "exotic" 'PROPAGATION_NESTED', which both seemed to be ignored. The DAO method is called from a controller (servlet), so I expect Spring to intercept that call. I must confess I haven't yet looked at detailed DEBUG-level logging from Spring to find out what is exactly happening.

      Originally posted by mdeinum View Post
      You can handle this in your dao, so you need to check in 1 place only. You could create a getRequiredById, if nothing is found throw an exception.
      Good idea. If I don't find a solution for my non-working declarative transaction this seems a perfectly reasonable workaround.

      Thanks a lot!

      Comment


      • #4
        Then I don't understand what your problem is?! If it is called from the controller a transaction should always start. Every method call to the dao will result into a transaction (unless specified different). REQUIRES_NEW or NESTED will only create a new transaction if a transaction is already created.

        Now if you call a method from the controller and that other method calls the getById method the getById method isn't going to spawn a new transaction.

        Also I don't see the solution in nested transactions for the issue you want to solve, even with a nested transaction you would still need to check for null in your controller. As soon as the method call is over the transaction ends, your OSIV isn't going to spawn a new transaction.

        The best solution is to create a getRequiredById (or maybe pass a boolean to your getById method to check if it is required or not) and throw a subclass of the DataAccessException.
        Last edited by Marten Deinum; Nov 26th, 2007, 07:53 AM.

        Comment


        • #5
          Originally posted by mdeinum View Post
          Then I don't understand what your problem is?!
          It is more curiosity than a problem. Let's compare the 2 situations where I have a 'getById()' DAO method that is directly called from a servlet. A Hibernate Session is bound to the thread by the Open Session In View filter.

          In the first situation the DAO method looks as follows, while no declarative transactions are defined for it:
          Code:
          public Model getByID(long modelID) {
          	Transaction tx = HibernateTemplate.getSessionFactory().getCurrentSession().beginTransaction();
          	tx.begin();
          	Model m = (Model) hibernateTemplate.load(Model.class, modelID);
          	tx.commit();
          	return m;
          }
          When called directly from the servlet, this method returns a proxy or _immediately_ throws an ObjectNotFoundException (ONFE) if the given id doesn't exist (which is what I expect).

          In the second situation the DAO method looks as follows:
          Code:
          public Model getByID(long modelID) {
          	return (Model) hibernateTemplate.load(Model.class, modelID);
          }
          and it is defined to be transactional in the configuration file:
          Code:
          <bean id="modelDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
          	<property name="transactionManager" ref="txManager" />
          	<property name="target" ref="hibModelDAO" />
          	<property name="transactionAttributes">
          		<props>
          			<prop key="addModel">PROPAGATION_REQUIRED</prop>
          			<prop key="deleteModel">PROPAGATION_REQUIRED</prop>
          			<prop key="updateModel">PROPAGATION_REQUIRED</prop>
          			<prop key="getByID">PROPAGATION_REQUIRES_NEW, readOnly</prop>
          		</props>
          	</property>
          </bean>
          I expected the same behavior as in the first situation, but to my surprise the ONFE is thrown only at the time that the "outer" transaction (i.e. the one related to the thread-bound Hibernate Session) is committed. When enabling DEBUG logging I see that the "inner" (getById) transaction is indeed committed, but no exception is thrown at that time. When comparing the logs of the 2 situations, the output (at tx commit time) is identical, except from the following statements:
          Code:
          09:37:33,942 DEBUG SessionImpl:422 - after transaction completion (identical in both situations)
          09:37:33,942 DEBUG HibernateTransactionManager:697 - Triggering afterCompletion
          synchronization
          09:37:33,942 DEBUG TransactionSynchronizationManager:265 - Clearing transaction
          synchronization
          09:37:33,958 DEBUG TransactionSynchronizationManager:185 - Removed value [org.sp
          ringframework.jdbc.datasource.ConnectionHolder@1ab51b0] for key [weblogic.jdbc.c
          ommon.internal.RmiDataSource@c6c084] from thread [[ACTIVE] ExecuteThread: '0' fo
          r queue: 'weblogic.kernel.Default (self-tuning)']
          09:37:33,958 DEBUG DataSourceUtils:201 - Resetting read-only flag of JDBC Connec
          tion [weblogic.jdbc.wrapper.PoolConnection_oracle_jdbc_driver_T4CConnection@68]
          09:37:33,958 DEBUG HibernateTransactionManager:666 - Not closing pre-bound Hiber
          nate Session [org.hibernate.impl.SessionImpl@3db1e] after transaction
          09:37:33,958 DEBUG SelectionController:71 - Finished Retrieving model from DAO (identical in both situations)
          Is there any explanation for this behavior?

          Comment


          • #6
            Is there any explanation for this behavior?
            Yes... The transaction commit happens on a different time.

            case one
            Code:
            	Transaction tx = HibernateTemplate.getSessionFactory().getCurrentSession().beginTransaction();
            	tx.begin();
            	Model m = (Model) hibernateTemplate.load(Model.class, modelID);
            	tx.commit();
            	return m;
            case two

            Code:
            begin transaction
            getById()
            commit transaction
            So there is a big difference in the transactional behavior.

            In the first case you start/finish the transaction yourself, effectivly bypassing the Spring stuff, you use Hibernate directly! The second case taps in the Spring TransactionManagement, to different cases.

            Comment


            • #7
              I understand the (slight) difference between the commit timing, but fail to understand why the exception is not thrown immediately after the transaction commits in the second case. Is the exception caught/handled by Spring Transaction Management?

              Anyway, I'll go with your 'getRequiredById' solution.

              Thanks a lot for your time spent on my questions!

              Comment


              • #8
                The first case completly by passes the Spring Transaction Management (and thus also the fact that you use OpenSessionInView). The second one uses the Spring Transaction Management.

                If you would rewrite your first case with direct access to the TransactionManager or the TransactionTemplate you would see the same behavior as with the declarative approach.

                Comment

                Working...
                X