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 />
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With