'@Transactional doesn't rollback by calling an External method that throw an RuntimeException

I'm playing with Spring and the @Transactional annotation. I'm doing a simple experiment to test the behaviour of this annotation. These are my super simple java classes:

my domain:

package com.xxx.springdemo.transactionalAnnotation.domain;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.Id;

@Data
@Entity
public class Counter {

    @Id
    int id;
    int count = 0;

}

my service:

package com.xxx.springdemo.transactionalAnnotation.services;

import com.xxx.springdemo.transactionalAnnotation.domain.Counter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class TransactionalService {

    public void throwException(Counter counter) {
        counter.setCount(1);
        throw new RuntimeException("Runtime exception");
    }

    public void dontThrowException(Counter counter) {
        counter.setCount(2);
    }
}

and my controller:

package com.xxx.springdemo.transactionalAnnotation.controllers;

import com.xxx.springdemo.transactionalAnnotation.domain.Counter;
import com.xxx.springdemo.transactionalAnnotation.services.TransactionalService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class TransactionController {

    private final TransactionalService transactionalService;

    @GetMapping("test-transactional")
    public void testTransactional() {
        Counter counter = new Counter();
        try {
            transactionalService.throwException(counter);
        } catch (Exception e) {
            System.out.println("Exception caught: " + e.getMessage());
        }
        System.out.println("Variable hasn't changed (counter = " + counter.getCount() + ")");
    }

    @GetMapping("test-transactional-2")
    public void testTransactionalWithoutException() {
        Counter counter = new Counter();
        try {
            transactionalService.dontThrowException(counter);
        } catch (Exception e) {
            System.out.println("Exception caught: " + e.getMessage());
        }
        System.out.println("Variable changed (counter = " + counter.getCount() + ")");
    }
}

The code should be self-explanatory. I expect to have printed:

Variable hasn't changed (counter = 0)

in case the test-transactional endpoint is called. What I get instead is:

Variable hasn't changed (counter = 1)

This means that in the service method throwException the count property isn't rollbacked after the exception is thrown.

P.S. this is my build.gradle file:

plugins {
    id 'org.springframework.boot' version '2.6.2'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'io.micrometer:micrometer-registry-prometheus'

    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'com.h2database:h2'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source