Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring @Transactional in an Aspect (AOP)

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!

like image 350
levacjeep Avatar asked Dec 30 '14 19:12

levacjeep


People also ask

Is @transactional an aspect?

The Transactional Aspect is an 'around' aspect that gets called both before and after the annotated business method. The concrete class for implementing the aspect is TransactionInterceptor .

Does transactional use AOP?

The combination of AOP with transactional metadata yields an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations.

What is the use of @transactional in Spring?

The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.

Can we use @transactional in repository?

The usage of the @Repository annotation or @Transactional . @Repository is not needed at all as the interface you declare will be backed by a proxy the Spring Data infrastructure creates and activates exception translation for anyway.


1 Answers

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.

like image 135
fps Avatar answered Oct 02 '22 08:10

fps