Search code examples
javaspring-boottransactionsaopaspectj

Why spring transactions dont work when i use EnableTransactionManegment(mode=Advice.ASPECTJ)?


I try to use Spring transactions whith AspectJ

**My Project: build.config

plugins {

    id 'org.springframework.boot' version '2.3.3.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    
    id "io.freefair.aspectj.post-compile-weaving" version "5.1.0"
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

project.ext {
    aspectjVersion = "1.8.2"
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework:spring-jdbc'
    implementation 'org.springframework:spring-tx'

    implementation 'org.postgresql:postgresql'

    implementation 'org.aspectj:aspectjrt'
    implementation 'org.aspectj:aspectjweaver'
    implementation 'org.aspectj:aspectjtools'
    implementation 'org.springframework:spring-aspects:5.3.2'
    implementation 'org.springframework:spring-instrument:5.3.2'

}

test {
    useJUnitPlatform()
}

DataConfig.class

   @Configuration
   @EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
   public class DataConfig {

     @Bean
     public DataSource postgres() {
         DriverManagerDataSource dataSource = new DriverManagerDataSource();
         dataSource.setDriverClassName("org.postgresql.Driver");
         dataSource.setUrl("jdbc:postgresql://localhost:5432/postgres?serverTimezone=UTC");
         dataSource.setUsername("postgres");
         dataSource.setPassword("admin");
         dataSource.setSchema("public");
         return dataSource;
     }

     @Bean
     public PlatformTransactionManager transactionManager() {
         return new DataSourceTransactionManager(postgres());
     }
  }

MainDAO.class

@Repository
public class MainDAO {
    private final JdbcTemplate jdbcTemplate;
    

    public MainDAO(DataSource postgres) {
        this.jdbcTemplate = new JdbcTemplate(postgres);
    }

    public Integer getSoundsCount() {
        return jdbcTemplate.queryForObject(
                "SELECT COUNT(*) FROM Sound", Integer.class);
    }


    @Transactional(propagation = Propagation.MANDATORY)
    public void insertSound() {
        insertAuthor();
        jdbcTemplate.update(
                "INSERT INTO Sound (author, name, id) VALUES (?,?,?)",
                0, "Spring", 0);
    }
}

When I call method insertSound from my service it run without exception. But it should throw exception becouse method insertSound have propogation.MANDATORY.

enter image description here

If i change Advice mode of EnableTransactionManegment to mode=Advice.PROXY then i get exception enter image description here

But with mode=Advice.ASPECTJ, transaction dont work.

I also try run application with annotation EnableLoadTimeWeaving and set library spring-instrument as java agent but it also transaction dont work:

enter image description here

enter image description here

What should i do for the transaction to work with mode=Advice.ASPECTJ ?


Solution

  • Load-time weaving (LTW)

    In order to make this work with LTW, you can use -javaagent:path/to/aspectjweaver.jar on the command line in combination with -javaagent:path/to/spring-instrument.jar and also add the @EnableLoadTimeWeaving you mentioned before to your configuration.

    Compile-time weaving (CTW)

    I found a way to make Spring's native AspectJ declarative transactions work with compile-time weaving, see my pull request. One key thing among other details is:

    @Bean
    public PlatformTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(h2());
        // Make native AspectJ declarative transactions work with compile-time weaving
        AnnotationTransactionAspect.aspectOf().setTransactionManager(transactionManager);
        return transactionManager;
    }
    

    See also this answer for reference.

    When running the application normally (i.e. without any -javaagent arguments because the transaction aspect has already been compiled in) after mvn compile, the console log should show Native AspectJ active = true every time URI sounds/add is called. That log line is triggered by this debug statement I added for illustration:

    @Transactional//(propagation = Propagation.MANDATORY)
    public void insertSound() {
        boolean nativeAspectjActive = Arrays
            .stream(new Exception().getStackTrace())
            .map(StackTraceElement::toString)
            .anyMatch(signature -> signature.contains("MainDAO.insertSound_aroundBody"));
        logger.info("Native AspectJ active = " + nativeAspectjActive);
        // (...)
    }