Announcement Announcement Module
Collapse
No announcement yet.
Unable to get @Transactional working Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Unable to get @Transactional working

    All,

    My app runs on JDK 1.5, Hibernate 3.0.5 and Spring 1.2.5

    I've been unable to get the @Transactional attribute working.

    The relevant part of my spring configuration is below:
    Code:
        <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
    
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
    
        <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
            <property name="transactionInterceptor" ref="transactionInterceptor"/>
        </bean>
    
        <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
            <property name="transactionManager" ref="txManager"/>
            <property name="transactionAttributeSource">
                <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>
            </property>
        </bean>
    My code looks like this:
    Code:
    public interface MyCrudService &#123;
       @Transactional
       public void create&#40;MyObject object&#41;;
    &#125;
    and the implementation is:
    Code:
    public class DefaultMyCrudService implements MyCrudService &#123;
       public void create&#40;MyObject object&#41; &#123;
             currentSession&#40;&#41;.save&#40;object&#41;;
       &#125;
    &#125;
    The currentSession() method obtains a hibernate session that has it's flush mode set to AUTO. However, if I don't use the annotation, and instead use an implementation like the one below, my tests pass:

    Code:
    public class DefaultMyCrudService implements MyCrudService &#123;
       public void create&#40;MyObject object&#41; &#123;
             Transaction transaction = currentSession&#40;&#41;.beginTransaction&#40;&#41;;
             currentSession&#40;&#41;.save&#40;object&#41;;
             transaction.commit&#40;&#41;;
       &#125;
    &#125;
    For my tests, I obtain the service from spring, so I'm sure that it will get advised correctly by the TransactionInterceptor.

    Am I doing something wrong here?

  • #2
    hi, pls post complete code for MyCrudService implementation. Currently it's missing the source for currentSession() method. Other than possible problems in this method, i don't see any other issues in your code.

    Comment


    • #3
      Here's the source code for the currentSession method

      Basically, MyCrudService has a session-per-thread implementation.
      I think the problem is defintely here - do I have to use SessionFactoryUtils to obtain a hibernate session?

      Code:
      public class MyCrudService &#123;
          private SessionFactory sessionFactory;
          private static ThreadLocal<Session> sessions = new ThreadLocal<Session>&#40;&#41;;
      
          public MyCrudService&#40;SessionFactory factory&#41;&#123;
              sessionFactory = factory;
          &#125;
      
          public void create&#40;MyObject object&#41; &#123;
              currentSession&#40;&#41;.save&#40;object&#41;;
          &#125; 
      
          private Session currentSession&#40;&#41; &#123;
              if &#40;sessions.get&#40;&#41; == null&#41; &#123;
                  sessions.set&#40;sessionFactory.openSession&#40;&#41;&#41;;
              &#125;
              return &#40;Session&#41; sessions.get&#40;&#41;;
          &#125;
      
          public void closeSession&#40;&#41; &#123;
              if &#40;sessions.get&#40;&#41;!= null&#41; &#123;
                  closeCurrentSession&#40;&#41;;
                  clearSession&#40;&#41;;
              &#125;
          &#125;
      
          private void closeCurrentSession&#40;&#41; &#123;
              currentSession&#40;&#41;.flush&#40;&#41;;
              currentSession&#40;&#41;.close&#40;&#41;;
          &#125;
      
          void clearSession&#40;&#41; &#123;
              sessions.remove&#40;&#41;;
          &#125;
      &#125;

      Comment


      • #4
        Keeping session in the thread's context is redundant as it's what essentially done by transactional context. In certain cases it might be dangerous too. In your particular case, I believe, you loose transactional context b/c you simply open new session w/o consulting SessioFactoryUtils which is .

        The normal workflow how you normally use SessionFactory/Session in Spring is:

        Code:
        public void setSessionFactory&#40;SessionFactory sessionFactory&#41; &#123;
          this.hibernateTemplate = new HibernateTemplate&#40;sessionFactory&#41;;
        &#125;
        
        public void create&#40;MyObject object&#41; &#123;
          hibernateTemplate.execute&#40;
            new HibernateCallback&#40;&#41; &#123;
              public Object doInHibernate&#40;Session session&#41;  &#123;
                 session.DO&#40;...&#41;;
                 ...
                 return null;
              &#125;
            &#125;
          &#41;;
        &#125;
        Alternatively (if you don't want to use template/callback) you can use SessionFactoryUtils for opening/releasing session to keep transactional context or extend HibernateDaoSupport.
        [/quote]

        Comment


        • #5
          Another way, if you want to avoid using Spring's API altogether, is to setup transaction manager for DataSource and simply use:
          Code:
          Session = sessionFactory.openSession&#40;&#41;;
          try &#123;
            session.DO&#40;&#41;;
            ...
            session.flush&#40;&#41;;
          &#125; finally &#123;
            session.close&#40;&#41;;
          &#125;
          and let all transactions be handled on the DataSource level. This way is not completely correct from the standpoint of Hibernate's API, but it works. It also has worse performance as it potentially opens/closes session many times per transaction.

          Comment


          • #6
            there's actually another better way to work with sessions in the transactional context:
            Code:
            public void create&#40;MyObject object&#41; &#123;
              sessionFactory.getCurrentSession&#40;&#41;.DO&#40;...&#41;;
            &#125;
            It also allows to avoid using Spring's API, but fully supports Spring's transaction management.

            Comment

            Working...
            X