@Transactional Spring not creating a new transaction
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.
![Shivam Sinha](https://i.stack.imgur.com/yJtt4.jpg?s=256&g=1)
Shivam Sinha
Updated on June 08, 2022Comments
-
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 almost 11 yearsHi 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 almost 11 yearsHi - 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 almost 11 yearsops 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