Spring @Transactional in Aspect (AOP)

I created an aspect containing the @Transactional annotation. My advice is invoked as expected, but the new AuditRecord object is never stored in 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 only intercepts interfaces (service interfaces). Service methods may or may not be transactional. If the service method is transactional, I would like this transaction to be discarded if for some reason an error occurred due to an error.

My question is: why is the transactional annotation ignored? This is my first time we created the AOP service with Spring, I also welcome any improvements in architecture or implementation.

Thanks!

+7
java spring-aop hibernate transactions
source share
2 answers

In Spring, @Transactional works by creating proxies of your class (Java or cglib proxies) and intercepting the annotated method. This means that @Transactional does not work if you call an 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) and it will work.

+9
source share

Very good question. If you need rollback / commit transactions, you can directly configure the spring transaction, as indicated here .

Executing this method does not require adding @Transactional for each class / method manually.

Configuration

Spring below (copied from the link). Instead of writing custom / pointcuts advisors, just add the configuration to the spring -application context.xml file with your / pointcuts advisors.

 <bean id="fooService" class="xyservice.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(* xyservice.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> 

link

+3
source share

All Articles