2014-12-30 33 views
7

Stworzyłem aspekt, który zawiera adnotację @Transactional. Moja rada jest przywoływana zgodnie z oczekiwaniami, ale nowa jednostka AuditRecord nigdy nie jest zapisywana w bazie danych, wygląda na to, że moja adnotacja @Transactional nie działa.Wiosna @ Transakcja w aspekcie (AOP)

@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; 
    } 

} 

Kontekst fasola jest następujący:

<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> 

Moja punktu przekroju jest przechwytywanie tylko interfejsy (interfejsy serwisowe). Metody serwisowe mogą być lub nie być transakcyjne. Jeśli metoda usługi jest transakcyjna, chciałbym, aby ta transakcja została wycofana, jeśli z jakiegoś powodu nie powiedzie się Porada.

Moje pytanie: Dlaczego adnotacja transakcyjna jest ignorowana? Jest to mój pierwszy projekt budowy usługi AOP na wiosnę. Z zadowoleniem przyjąłbym także wszelkie ulepszenia architektoniczne lub wdrożeniowe.

Dzięki!

Odpowiedz

9

Wiosną @Transactional działa poprzez utworzenie proxy klasy (Java lub proxy Cglib) i przechwycenie metody z adnotacjami. Oznacza to, że @Transactional nie działa, jeśli wywołujesz metodę z adnotacjami z innej metody tej samej klasy.

Po prostu przenieś metodę createRecord do nowej klasy (nie zapomnij także o fasoli Spring) i to zadziała.

3

Bardzo dobre pytanie. Jeśli musisz wycofać/zatwierdzić transakcje, możesz bezpośrednio skonfigurować transakcję wiosenną, tak jak wspomniano, here.

Wykonanie tej metody nie wymaga ręcznego dodawania @Transactional dla każdej klasy/metody.

Konfiguracja sprężyny znajduje się poniżej (skopiowana z odnośnika). Zamiast pisać niestandardowych doradców/pointcuts, po prostu dodaj konfigurację w pliku context.xml aplikacji Spring do swoich doradców/wskazówek.

<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:

  1. Wiosna transakcyjna: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html