I am using spring jpa. I have implemented transaction management using
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'find' are read-only -->
<tx:method name="find*" read-only="true" />
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="transactionalServiceOperation"
expression="execution(* com.test..*ServiceImpl.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionalServiceOperation" />
</aop:config>
Recently i noticed that although in some cases transaction management is working but in other cases it is not. For example , i have this piece of code inside a service method which is annotated by #org.springframework.transaction.annotation.Transactional.
The code inside the method
guardianService.save(newFather);
parentsSet.add(newFather);
Guardian oldMother = guardianService.findById(motherId);
In the above case the data in the database does not persis till the 3rd line. As soon as the application finishes executing the 3rd line the data of newFather gets comited to the database even if the application generates Exception after the 3rd line.
Code snippet of guardianService.findById(motherId)
#Override
public Guardian findById(long guardianId) {
return guardianRepository.findByGuardianId(guardianId);
}
Inside GuardianRepository
public interface GuardianRepository extends JpaRepository<Guardian, Long> {
Guardian findByGuardianId(long id);
}
Code snippet of guardianService.save(newFather);
#Override
#Transactional
public Guardian save(Guardian guardian) {
return guardianRepository.save(guardian);
}
So my question is does the find() method somehow calls the flush() or commit()?
The find() method actually does invoke the flush method. By default in JPA, FlushModeType is set to AUTO, which means that if query to database occurs, the data in database has to be up to date for current transaction. From definition:
When queries are executed within a transaction, if FlushModeType.AUTO
is set on the Query object, or if the flush mode setting for the
persistence context is AUTO (the default) and a flush mode setting has
not been specified for the Query object, the persistence provider is
responsible for ensuring that all updates to the state of all entities
in the persistence context which could potentially affect the result
of the query are visible to the processing of the query.
Related
I've created an Aspect which contains an #Transactional annotation. My advice is being invoked as expected, but the new entity AuditRecord is never saved to the database, it looks like my #Transactional annotation is not working.
#Aspect
#Order(100)
public class ServiceAuditTrail {
private AppService appService;
private FooRecordRepository fooRecordRepository;
#AfterReturning("execution(* *.app.services.*.*(..))")
public void logAuditTrail(JoinPoint jp){
Object[] signatureArgs = jp.getArgs();
String methodName = jp.getSignature().getName();
List<String> args = new ArrayList<String>();
for(Object arg : signatureArgs){
args.add(arg.toString());
}
createRecord(methodName, args);
}
#Transactional
private void createRecord(String methodName, List<String> args){
AuditRecord auditRecord = new AuditRecord();
auditRecord.setDate(new Date());
auditRecord.setAction(methodName);
auditRecord.setDetails(StringUtils.join(args, ";"));
auditRecord.setUser(appService.getUser());
fooRecordRepository.addAuditRecord(auditRecord);
}
public void setAppService(AppService appService) {
this.appService = appService;
}
public void setFooRecordRepository(FooRecordRepository fooRecordRepository) {
this.fooRecordRepository= fooRecordRepository;
}
}
The bean context is as follows:
<tx:annotation-driven transaction-manager="txManager.main" order="200"/>
<aop:aspectj-autoproxy />
<bean id="app.aspect.auditTrail" class="kernel.audit.ServiceAuditTrail">
<property name="appService" ref="app.service.generic" />
<property name="fooRecordRepository" ref="domain.repository.auditRecord" />
</bean>
My pointcut is intercepting only interfaces (service interfaces). The service methods may or may not be transactional. If the service method is transactional, I would like that transaction to be rolled back if the Advice fails for some reason.
My question: Why is the transactional annotation being ignored? This is my first time building an AOP service with Spring, I would also welcome any architectural or implementation improvements.
Thanks!
In Spring, #Transactional works by creating a proxy of your class (either a Java or cglib proxy) and intercepting the annotated method. This means that #Transactional doesn't work if you are calling the annotated method from another method of the same class.
Just move the createRecord method to a new class (don't forget to make it a Spring bean too) and it will work.
A very good question. If you have to rollback/commit the transactions you can directly configure the spring transactional as mentioned here.
Doing this method does not require you to add #Transactional on each of the class/method manually.
Spring configuration is below(copied from the reference link ). Instead of writing custom advisors/pointcuts just add configuration in spring-application context.xml file with your advisors/pointcuts.
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<!-- don't forget the DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<!-- similarly, don't forget the PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
ref:
Spring transactional : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html
I've got some trouble while configuring spring transaction manager. The application I'm working on has a layered architecture. Therefore it has a service layer containing all the transactional services. I wanted for spring to rollback the transaction when a checked (application specific) exception has occurred. I succeeded doing that by annotation as follows:
#Transactional(value="transactionDds", rollbackfor="Throwable")
This works fine. But since I've so many services hence I want to move this configuration to XML (spring DAO context file). This is what I've done:
<tx:advice id="txAdvice" transaction-manager="transactionManagerDds">
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="transRollbackOp" expression="execution(*fr.def.iss.ult.moduleManagement.service.dds.*.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transRollbackOp"/>
</aop:config>
<bean id="transactionManagerDds" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="beanDataSourceFactory" />
<qualifier value="transactionDds"/>
</bean>
So basically, I'va a transaction manager which is associated to an advice which rolls back for the possible methods when a Throwable exception occurs. And this advice is linked to an AOP config that way we specify all the interfaces in the service layer of application on which this transaction configuration is to be applied. But this doesn't work. Transaction doesn't roll back Throwable exception occurs. I don't understand that this works with annotation but not with declarative configuration in XML.
I'm really looking forward for your suggestions. I'm totally blocked. Plz help me out. Thanx in advance.
<aop:config>
<aop:pointcut id="transRollbackOp" expression="execution(*fr.def.iss.ult.moduleManagement.service.dds.*.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transRollbackOp"/>
</aop:config>
In your <aop:config /> the expression you entered is invalid. It should at least contain a whitespace between * and fr.def.
Next instead of .*.*.* I suggest writing ..*.* which selects all classes in all subpackages regardless of depth.
In short change your expression to execution(* fr.def.iss.ult.moduleManagement.service.dds..*.*(..)) should make it work.
we are using Spring and hibernate in our project and i have noticed that we have defined <tx:annotation-driven /> in sprint context file and if i put #Transactionalannotation for method i am successfully able to interact with database but if i remove #Transactional annotation from method i am getting exception stating that "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here"
is there any way by which i can define all the select operation as non Transactional(i mean without #Transactional annotation) and update and insert as Transactional
Using #Transactional(readOnly=true) is usually the best thing for select-only operations. Using hibernate in auto-commit mode can be wasteful when multiple select statements are needed to complete an operation.
I am able to resolve this issue by using below code in context xml file.
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="allServiceMethods" expression="execution(* com.test.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allServiceMethods"/>
</aop:config>
As by default read-only="false" for #Transactional annotation.
So we need to make it as read-only="true" for all read operation(Thanks Affe for pointer)
to set read-only="true" for all select select operation make sure that you start your method name from get and rest below code will code for us. you can change your below configuration as per your need.
<tx:method name="get*" read-only="true"/>
For more information you can refer to below link
Spring Docs
I'm using java 1.6 and spring 3.0.4, I want to realize a java functionality that
calculate new data values
update one-by-one the existing values on the database
If in any of this step there's an error I want to rollback the whole transaction and come back to the previous state.
I already realized all this pieces of code, I just want to put them together. How I can manage this with the existing spring values that are working with #Entity and #Column annotations?
Thanks!
Short answer: as you're using Spring, the easiest would be to use the transaction management, creating a service that represents this transaction unit and annotate the method with #Transactional
In practice, you need to configure a PlatformTransactionManager in your application. As you seem to use JPA, the JpaTransationManager seems like an obvious choice. To enable the processing of the #Transactional annotation, you can either use #EnableTransactionManagement or the <tx:annotation-driven/> namespace. Both are explained in the Javadoc of #EnableTransactionManagement
By default, a runtime exception thrown from that annotated method will manage a transaction rollback. If your code is using checked exceptions, you'll have to configure the rollbackFor attribute of the annotation.
There are more details and examples available in the documentation
For people that need the same configuration, here you can find how I solved this problem, integrating Hibernate with Spring.
<!-- session factory activate the transaction modules for the specified classes -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="configLocation" value="classpath:config-hibernate.xml" />
<property name="packagesToScan">
<list>
<!-- Additional packages required to be added if entities located elsewhere -->
<value>com.some.package.dao</value>
<value>com.some.package.model</value>
<value>com.some.package.SpecificClass</value>
</list>
</property>
<property name="mappingResources" ref="mappingResources"/>
<bean id="mappingResources" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<!-- here you can add your hibernate mapping query that you want to use on transaction -->
<value>config-hibernate-mapping.xml</value>
</list>
</property>
</bean>
<!-- This will activate transactional annotation -->
<tx:annotation-driven transaction-manager="transactionManager" />
#Service
#Transactional
public class SpecificClass {
// write your method, everyone of them will be transactional
// and there will be a commit in case of success or rollback in case of exception
}
<tx:advice id="txAdvice" transaction-manager="jtaTxManager">
<tx:attributes>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="fooServiceOperation"
expression="execution(* x.y.SampClass.save(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation" />
</aop:config>
<bean id="Samp1" class=" x.y.SampClass"></bean>
<bean id="SearchDispatchRpcGwtServlet" class="x.y.server.SearchDispatchRpcGwtServlet">
<constructor-arg>
<list>
<ref bean="webServiceClient"/>
</list>
</constructor-arg>
</bean>
<!-- Service Clients -->
<bean id="webServiceClient" class="x.y.KSBClientProxyFactoryBean">
<property name="serviceEndpointInterface" value="x.y.service.WebService" />
<property name="serviceQName" value="{http://x.y.org/wsdl/organization}WebService" />
</bean>
This is the part of sample spring context file. I am trying to create a transaction advice for SampClass to execute on the save method.
So from my understanding it should create a proxy for SampClass only.
I have a SearchDispatchRpcGwtServlet which takes as an argument a webservice Client which is also a proxy in itself. This bean is also getting proxied for some reason where it fails because it cannot create a proxy of a proxy.
I must add that SearchDispatchRpcGwtServlet creates an instance of SampClass and calls the save method.
I get the following exception:
java.lang.IllegalArgumentException:
Cannot subclass final class class
$Proxy118 at
net.sf.cglib.proxy.Enhancer.generateClass(Enhancer
.java:446) at
net.sf.cglib.transform.TransformingClassGenerator.
generateClass(TransformingClassGenerator.java:33)
I dont understand why SearchDispatchRpcGwtServlet proxy is getting created. Can someone explain.
Addition Stack Trace
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class $Proxy117]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy117
at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:213)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:473)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:348)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:309)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:361)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1427)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport$1.run(FactoryBeanRegistrySupport.java:139)
... 85 more
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy117
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
at net.sf.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:201)
a subclass of AbstractAutoProxyCreator is trying to proxy a bean, but that bean is already itself a CGLIB proxy created using another mechanism such as aop:config proxy-target-class="true.
To avoid this, make sure to avoid mixing the different aspect weaving mechanisms (ProxyBeanFactory and aop:config ) for the same bean.
In this case the transaction aspects can also be weaved via <tx:annotation:driven/> via the #Transactional annotation.
Alternatively it would be possible to remove the autowiring/scan from the bean and do the injection of dependencies via XML using setters.
Another alternative is to use load time weaving everywhere by declaring <context:load-time-weaver/> and adding the needed jars.
Have a look also at this post, it general it would be better to use only one one way of applying the aspects in the whole application, in order to avoid this problem.