I try to execute a transactional operation and intentionally throw an exception in order to verify if a rollback is done but the rollback isn't being executed.
The PostgreSQL database version is 12.1-1 and is Docker-based.
Here is the service that contains the @Transactional
annotation:
@Service
public class MyTestService {
@Autowired
private DocumentDataDao documentDataDao;
@Transactional
public void test() {
DocumentData data = new DocumentData();
data.setData(UUID.randomUUID().toString());
documentDataDao.create(data);
throw new IllegalArgumentException("Test rollback");
}
}
The create
function is using a NamedParameterJdbcTemplate
to insert the data:
String statement = String.format("INSERT INTO %s (%s) VALUES (%s) RETURNING %s", tableName,
String.join(",", insertingColumns), String.join(",", values),
String.join(",", returningColumns));
return getNamedJdbcTemplate().queryForObject(statement, parameters, getRowMapper());
And the test
function is called from another service:
@Service
public class ApplicationStartupListener {
private Logger log = LoggerFactory.getLogger(ApplicationStartupListener.class);
@Autowired
private MyTestService testService;
@PostConstruct
public void init() {
try {
testService.test();
} catch (Exception e) {
log.error("fail to start", e);
}
}
}
When debugging I found out that if the rollback isn't executed it's because of the transaction being IDLE
.
Here is the rollback
function from PgConnection
and executeTransactionCommand
isn't being executed:
public void rollback() throws SQLException {
checkClosed();
if (autoCommit) {
throw new PSQLException(GT.tr("Cannot rollback when autoCommit is enabled."),
PSQLState.NO_ACTIVE_SQL_TRANSACTION);
}
if (queryExecutor.getTransactionState() != TransactionState.IDLE) {
executeTransactionCommand(rollbackQuery);
}
}
Any hint on why the transaction is being marked as idle and stops the rollback method to be executed would be appreciated.
Edit (1)
As @M. Deinum mentioned, there is no guarantee that a transactional proxy has been created when using @PostConstruct
. That's why I tested with an ApplicationRunner
:
@Component
public class AppStartupRunner implements ApplicationRunner {
@Autowired
private MyTestService testService;
@Override
public void run(ApplicationArguments args) throws Exception {
testService.test();
}
}
But this didn't work either.
I also tried to run the test
method a few moments after the application has been started by using a RestController
and sending an HTTP request to it but still, the same issue.
@RestController
public class AppController {
@Autowired
private MyTestService testService;
@GetMapping("/test")
public ResponseEntity<Object> test() {
testService.test();
return ResponseEntity.ok().build();
}
}
Edit (2)
I upgraded the PostgreSQL JDBC version from 42.2.2 to 42.2.18 (latest as of now) but the connection is still IDLE when trying to rollback.
Edit (3)
I reproduced the issue in a git repository: https://github.com/Martin-Hogge/spring-boot-postgresql-transactional-example/tree/master.
@PostConstruct
. You should use a proper listener or use anApplicationRunner
which executes after everything has started has been fully initialized. – M. Deinumidle
transaction is not a problem and does not indicate there is a n open transaction.idle in transaction
would indicate an open transaction. – a_horse_with_no_name