@Transactional Spring not creating a new transaction

12,882

As you noticed, declarative transaction management is AOP based. This means that Spring wraps the transactional beans into a transactional proxy, which takes care of starting and committing transactions. This means that the method call must be intercepted by the proxy in order to be transactional.

It looks like you're calling this method directly from another method of the same bean. In that case, it's a direct call, which doesn't go through the proxy, which thus can't start the transaction:

HTTP request --> controller.someUnknownMethod() --> controller.save()

The transactional method should be in a separate Spring bean (a service), called by the controller. This will make the call go through the proxy:

HTTP request --> controller.someUnknownMethod() --> transactional proxy --> service.save()

This is explained in details in the documentation.

Share:
12,882
Shivam Sinha
Author by

Shivam Sinha

Updated on June 08, 2022

Comments

  • Shivam Sinha
    Shivam Sinha about 2 years

    Any help with this will be greatly appreciated. Iam using Spring, Jetty(Eclipse Jetty Plugin). It seems like declarative transaction management @Transactional is not working.

    Confirmed this isn't working in two ways: 1. The newly created entity is not flushed into the database after the method call. 2. TransactionSynchronizationManager.isActualTransactionActive(); returns false

    Note the method below is apart for @Controller class, where a http request calls eventually calls this save method. The controller class does not implement any interfaces. Actual Code:

     @Transactional(propagation = Propagation.REQUIRES_NEW)
      public void save(PERule peRule) {
         boolean inTransaction = TransactionSynchronizationManager.isActualTransactionActive();
                peRuleDAOImpl.persist(peRule);
         }
    

    The lack of transaction is also confirm by the Log Output:

    DEBUG: org.hibernate.internal.SessionImpl - Opened session at timestamp: 13762325621
    DEBUG: org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl - Skipping
    JTA sync registration due to auto join checking
    DEBUG: org.hibernate.ejb.AbstractEntityManagerImpl - Looking for a JTA transaction to join
    DEBUG: org.hibernate.ejb.AbstractEntityManagerImpl - Unable to join JTA transaction
    DEBUG: org.hibernate.event.internal.AbstractSaveEventListener - Delaying identity-insert due to no transaction in progress
    

    However when I programatically define explicit transaction boundaries and commit the transaction, the entity is is flushed in to the database. E.g:

    @Resource
    private PlatformTransactionManager txManager; 
    private void save(PERule peRule) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = txManager.getTransaction(def);
        def.setName("SomeTxName");
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        try {
            peRuleDAOImpl.persist(peRule);
        } catch (Exception ex) {
            txManager.rollback(status);
        }
        txManager.commit(status);
    }
    

    Hence this doesn't seem to be a problem with the way I defined my transactionManager, since it can be inject into the object as defined above.

    ContextConfig: root-context.xml

    <aop:aspectj-autoproxy />  
    <context:annotation-config />
    
     <context:component-scan
            base-package="org.springframework.samples.mvc, com.project.*" />
    
    
     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="location">
                  <value>/WEB-INF/spring/proddatabase.properties</value>
             </property>
    </bean>
     <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiName" value="jdbc/myds" />
    </bean>
    
      <bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
       <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitName" value="EconDatesDB" />
        <property name="persistenceXmlLocation" value="/WEB-INF/spring/jpa-prod-persistence.xml" />
        <property name="jpaVendorAdapter">
             <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                        <property name="generateDdl" value="false" />
                        <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
       </property>
            <property name="jpaProperties">
                  <props>
                        <prop key="hibernate.format_sql">false
                        </prop>
                        <prop key="hibernate.use_sql_comments">false
                        </prop>
                        <prop key="hibernate.generate_statistics">false
                        </prop>
                        <prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
                        </prop>
                        <!-- The following line is what's used in Hibernate 4 instead of a TransactionManagerLookup 
                              class -->
                        <prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
                        </prop>
    
                  </props>
            </property>
      </bean>
      <tx:annotation-driven mode="aspectj"  proxy-target-class="true" transaction-manager="transactionManager"/>
    
    
      <bean id="transactionManager"
            class="org.springframework.transaction.jta.JtaTransactionManager">
            <property name="transactionManager" ref="atomikosTransactionManager" />
            <property name="userTransaction" ref="atomikosUserTransaction" />
      </bean>
    
      <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
            <property name="transactionTimeout" value="300" />
      </bean>
      <!-- Construct Atomikos UserTransactionManager, needed to configure Spring -->
      <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
            init-method="init" destroy-method="close">
            <!-- when close is called, should we force transactions to terminate or 
                  not? -->
            <property name="forceShutdown" value="false" />
            <property name="transactionTimeout" value="300" />
      </bean>
    
    
      <mvc:annotation-driven />
    
      <beans:bean id="conversionService"
            class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
            <beans:property name="formatters">
                  <beans:bean
                        class="org.springframework.samples.mvc.convert.MaskFormatAnnotationFormatterFactory" />
            </beans:property>
      </beans:bean>
    
      <!-- Controllers.xml Only contains routes from path to view name, and has no other spring config
      <mvc:view-controller path="showcase/" view-name="/WEB-INF/views/home" /> -->
      <beans:import resource="/appServlet/controllers.xml" />  
    

    servlet-context:

          <resources mapping="/resources/**" location="/resources/" />
      <resources mapping="/sharedResources/**" location="/parkingEngine/resources/" />
    
      <beans:bean id="viewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <beans:property name="order" value="1" />
            <!-- <beans:property name="prefix" value="/WEB-INF/views/" /> -->
            <beans:property name="prefix" value="" />
            <beans:property name="suffix" value=".jsp" />
      </beans:bean>
    

    web.xml:

          <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/root-context.xml</param-value>
      </context-param>
    
    
      <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
    
      <servlet>
            <servlet-name>appServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
      </servlet>
    
      <servlet-mapping>
            <servlet-name>appServlet</servlet-name>
            <url-pattern>/</url-pattern>
      </servlet-mapping>
    

    pom.xml relevant dependencies:

    <dependency>
                  <groupId>cglib</groupId>
                  <artifactId>cglib-nodep</artifactId>
                  <version>2.2</version>
            </dependency>
            <dependency>
                  <groupId>org.aspectj</groupId>
                  <artifactId>aspectjweaver</artifactId>
                  <version>1.6.10</version>
            </dependency>
            <dependency>
                  <groupId>org.aspectj</groupId>
                  <artifactId>aspectjrt</artifactId>
                  <version>1.6.10</version>
            </dependency>
    

    I have also attempted following variations of tx:annotation-driven in my root-context.xml:

    <tx:annotation-driven mode="aspectj"  proxy-target-class="true" transaction-manager="transactionManager"/>
    <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
    <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
    <tx:annotation-driven />
    
  • Shivam Sinha
    Shivam Sinha almost 11 years
    Hi as suggested I created a service interface, implemented the interface(ServiceImpl) and injected the DAO(which contained the transactional save method) into it. Injected the ServiceImpl into the controller class and it still produce the same result. Its as if the proxy classes arent being created. Iam using the standard eclipse project>>clean and then running the plugin which starts the server.
  • Shivam Sinha
    Shivam Sinha almost 11 years
    Hi - I removed the transactionManager, atomikosUserTransaction & atomikosTransactionManager from my root-context. Configured them in my Jetty-Env.xml, with JNDI enabled and put <tx:annotation-driven "/> so that spring could auto-discover these in the JNDI container. This also with suggestion made above worked. thanks
  • Shivam Sinha
    Shivam Sinha almost 11 years
    ops correction <tx:annotation-driven /> was already in my root-context.xml . Put <tx:jta-transaction-manager /> in my root-context for auto discovery of jetty jndi transaction manager