2

I'm using Spring boot 2.5.5 with AspectJ 1.9.7 (CTW). I've spotted that sometimes transactions don't roll back and to fix that I need only recompile code and run it again. For example:

I have method addB() persisting entity B, method addC() throwing exception and method A() combining them. When I call A(), exception is thrown, but entity B stays in database (as expected). When I annotate method A() with @Transactional result is the same. But if I build everything again (without any changes) then transaction is being rollbacked and there is no new record in database.

Here is my full POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.abcc</groupId>
    <artifactId>abcc</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>abcc</name>
    <description>abcc</description>
    <properties>
        <java.version>11</java.version>
        <log4j2.version>2.15.0</log4j2.version>
        <aspectj.version>1.9.7</aspectj.version>
        <aspectj-maven-plugin.version>1.14.0</aspectj-maven-plugin.version>
    </properties>
    <dependencies>
        <!--SPRING-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!--CACHE-->
        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>3.8.1</version>
        </dependency>
        <!--DATABASE-->
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.18.1</version>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.19.Final</version>
        </dependency>
        <dependency>
            <groupId>org.passay</groupId>
            <artifactId>passay</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>2.4.2</version>
        </dependency>
        <dependency>
            <groupId>com.sanctionco.jmail</groupId>
            <artifactId>jmail</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.13</version>
        </dependency>
        <!--AOP-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
    </dependencies>

    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <activatedProperties>dev</activatedProperties>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <id>test</id>
            <properties>
                <activatedProperties>test</activatedProperties>
            </properties>
            <dependencies>
                <dependency>
                    <groupId>com.h2database</groupId>
                    <artifactId>h2</artifactId>
                    <version>1.4.191</version>
                </dependency>
            </dependencies>
        </profile>
    </profiles>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                    <excludeDevtools>false</excludeDevtools>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <trimStackTrace>false</trimStackTrace>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>${aspectj-maven-plugin.version}</version>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjrt</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <showWeaveInfo>true</showWeaveInfo>
                    <Xlint>ignore</Xlint>
                    <verbose>true</verbose>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <complianceLevel>${java.version}</complianceLevel>
                    <encoding>utf-8</encoding>
                    <outxml>true</outxml>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.springframework</groupId>
                            <artifactId>spring-aspects</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                    <excludes>
                        <exclude>**/*.java</exclude>
                    </excludes>
                    <forceAjcCompile>true</forceAjcCompile>
                    <sources/>
                    <weaveDirectories>
                        <weaveDirectory>${project.build.outputDirectory}</weaveDirectory>
                    </weaveDirectories>
                </configuration>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>

It's very strange situation, which I can't find information on. Could you help with finding reason to it?

kriegaex
  • 63,017
  • 15
  • 111
  • 202
Shibitos
  • 23
  • 4
  • I would be glad to help if I had an [MCVE](https://stackoverflow.com/help/mcve) on GitHub in order to reproduce the problem. I already have an idea what might be happening here, but want to see for myself instead of speculating. Let your MCVE be something really simple, either without database or with an in-memory one which is started and initialised automatically. BTW, are you talking about Maven builds or IDE build which you have to repeat? – kriegaex Dec 22 '21 at 01:54
  • Maven build, started by run configuration in IntelliJ 2021.2.3. Command "clean install -DskipTests -T 6" with profile "dev". I will provide MCVE in comment. – Shibitos Dec 22 '21 at 20:08
  • Here is MCVE: https://github.com/Shibitos/MCVE-jv21122021 – Shibitos Dec 22 '21 at 20:59

2 Answers2

2

I cannot reproduce the problem because IDEA does not find the Lombok setters. Even when delegating build actions before run to Maven, I get NoSuchMethodError: '...TestEntity.setCode(java.lang.String)'. Next, I am going to try without Lombok. Please note that Lombok and AspectJ do not play nice with each other, see my answer here. Alternatively, you could also make sure that Maven does either of these:

  1. First build with Javac + Lombok, then apply AspectJ binary weaving in a second step, all in one module.
  2. Similar to above, but do the first build step in module A and the second one in a separate module B. Then you have an unwoven and a woven artifact, which you can both use according to your preferences. For example, you could also use the unwoven one and apply transaction aspects via load-time weaving (LTW) while starting the application. See my other answer here for both approaches #1 and #2.
  3. Delombok the source code build the generated sources with the AspectJ compiler in a second build step.

I generated constructors, getters and setters in the IDE instead of using Lombok. Now the project compiles in both IDE and Maven. It behaves exactly as it should. With @Transactional, 0 entities are created, without it 2.

I am not sure if Lombok vs. AspectJ really is the problem due to non-compileability when using Lombok annotations, but it should be easy enough to try without Lombok for you. If it works in your context, too, we found the culprit and can think about implementing one of the 3 approaches mentioned above. Then you can tell me if you have any difficulty in doing so.


Update: I created the two-module version - Javac + Lombok, then Aspect weaving - for you in my fork and also issued pull request #1. I also improved testability a bit. See if that works for you.

Caveat: You cannot simply run DemoApplication from the application-lombok module, because that module is still unwoven and will not show transactional behaviour. But you can simply change the classpath for the run config to the application-aspectj module:

IntelliJ IDEA run configuration


Update: As we found out in the comment section of the other answer, in addition to the problematic Lombok vs. AspectJ compiler configuration, the OP also simply had a problem with his IDE: Using IntelliJ IDEA Community Edition, he was first unaware of, then unable to install the AspectJ plugin, which means that IDEA does not know antyhing about the AspectJ compiler and simply overwrites anything which might have been compiled by AspectJ Maven before with plain Java classes. Therefore, transactional aspects do not work either, unless

  • either pre-run compilation is disabled and mvn compile started as an additional pre-build step for the corresponding run configuration,
  • or all build actions for the project are being delegated to Maven via configuration,
  • the OP buys a licence of IDEA Ultimate and installs the AspectJ plugin.
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Please note my update with regard how to run the demo application from IDEA, using the right module. – kriegaex Dec 23 '21 at 04:15
  • **Update 2:** I explained the IntelliJ IDEA problem with the missing AspectJ plugin for the Community Edition in order to give a complete picture of both problems in this question. – kriegaex Dec 27 '21 at 01:03
0

First of all: your multi-module approach works perfectly in my environment. But then I checked initial MCVE and when I completely removed Lombok, strange behaviour did not disappear. When reading your answers (this one) I have checked "Delegate IDE build/run actions to maven" in IntelliJ settings (Build Tools -> Maven -> Runner) and it started working as it should. In next step I switched this option off and checked "Do not build before run" in run configuration. I do not understand it completely (especially why did it work in old way after second try), but your comment helped me to reach that.

I will research IntelliJ behaviour (in two scenarios console output is almost identical), but if you have idea why does it work like that I would be glad to hear it. You helped me a lot, thank you!

Summary solution: I have enabled "Do not build before run" in IntelliJ run configuration for my application. Now changes works after first build.

Shibitos
  • 23
  • 4
  • Well, if you have to disable the build, then it is a sign that something is wrong with the build configuration. You should fix it rather than disable it. Furthermore, like I said, I cannot reproduce the original problem due to the problematic Lombok situation in the context of Maven auto-import. So I guess you are not using Maven auto-import and have some kind of manual build configuration not derived from Maven. Ergo, you have an IDEA problem here. But your original Maven project was broken, too. The build failed due to Lombok + AspectJ. – kriegaex Dec 23 '21 at 12:43
  • I have pushed version without Lombok, where problem still occurs. How can I check it your way? Right now I'm opening project with IntelliJ, adding Maven run configuration with command line: "clean install -DskipTests" and then running it with Application run configuration. Even if I build and run it from console using "mvnw spring-boot:run" everything works. The only case is when I'm using Application run configuration with default settings. Delegating build/run actions to maven solves problem too. – Shibitos Dec 23 '21 at 13:38
  • 1
    The only way I can reproduce your problem is to manually select **_Javac_** instead of **_Ajc_** in **"File | Settings | Build, Execution, Deployment | Compiler | Java Compiler"**. Normally this should be set correctly during Maven auto-import. Did you forget to install the **AspectJ plugin for IntelliJ IDEA?** Then of course, the IDE would always compile POJOs and not weave any aspects. In that case, you really would have to disable auto-compilation for your run config and always build with Maven first. Or you delegate builds to Maven. But better just install the plugin. – kriegaex Dec 24 '21 at 01:09
  • I didn't know about this plugin (I just started learning AspectJ). Unfortunately, I'm using community version of IntelliJ and [this](https://plugins.jetbrains.com/plugin/4679-aspectj) plugin requires ultimate one. It seems that Ajc is available only in ultimate too. I guess I'll have to stick with pure maven, until I decide to upgrade. – Shibitos Dec 25 '21 at 10:20