Databases 14 min read

Why Did My Spring @Transactional Rollback Fail? Uncovering a MySQL Connector Bug

An in‑depth investigation reveals that Spring’s @Transactional rollback can silently fail due to a bug in mysql‑connector‑java 8.0.28 when useLocalSessionState=true, causing auto‑commit to stay enabled; the article explains the root cause, debugging steps, and how upgrading to 8.0.29 resolves the issue.

Programmer DD
Programmer DD
Programmer DD
Why Did My Spring @Transactional Rollback Fail? Uncovering a MySQL Connector Bug

Background

The article begins by introducing the components involved: commons-db , an internal Spring‑based multi‑datasource management library; a java‑project used for functional testing (Spring Boot 2.5.4, mysql‑connector‑java 8.0.26); a store project (Spring Boot 2.6.6, mysql‑connector‑java 8.0.28); and an Alibaba Cloud RDS instance (default isolation READ_COMMITTED, MySQL default REPEATABLE_READ).

Problem

In the store project, a method annotated with @Transactional and @DataSource throws a runtime exception (division by zero), but the inserted user record is still persisted, indicating that the transaction rollback does not take effect.

@Transactional
@DataSource(type = Type.MASTER, value = "developer")
public void addUser(ApolloUser user) {
    userRepository.save(user);
    int i = 1/0; // throws exception
}

Assumptions

Assumption 1: The AOP proxy for @Transactional might not be active. Debug logs show the transaction lifecycle, so this is false.

Assumption 2: The commons-db component could be using different connections for commit and rollback. Logs show the same connection is used, so this is also false.

Turning Points

Turning Point 1

Running the same code in java‑project (same MySQL version) rolls back successfully, suggesting the issue is environment‑specific.

Turning Point 2

Setting the transaction isolation level to Isolation.REPEATABLE_READ in store makes rollback work, but the same isolation level in java‑project already works, so isolation alone is not the root cause.

@Transactional(isolation = Isolation.REPEATABLE_READ)
@DataSource(type = Type.MASTER, value = "developer")
public void addUser(ApolloUser user) {
    userRepository.save(user);
    int i = 1/0;
}

Turning Point 3

Changing isolation to READ_UNCOMMITTED also makes rollback succeed, confirming that isolation is not the decisive factor. The investigation then focuses on the JDBC driver behavior.

Analysis

A typical Spring transaction creates a connection with autoCommit = false, executes the method, and on failure calls doRollback. The logs show these steps, so the problem must lie deeper, possibly in the driver.

Locating the Problem

Debugging the MySQL driver’s NativeSession.execSQL() reveals that when rollback fails, the driver never sends SET autocommit=0, leaving the connection in auto‑commit mode. Consequently, the insert is committed before the rollback can occur.

Decryption

The driver version 8.0.28 introduced a change where isAutocommit() checks a statusFlags field instead of the autoCommit flag. With useLocalSessionState=true, the driver incorrectly reports isAutocommit() as false, causing the transaction manager to skip the SET autocommit=0 command.

Reproducing the Issue

Upgrade Spring Boot to 2.6.6 (matching store ) – the bug reproduces.

Keep Spring Boot 2.5.4 and upgrade mysql‑connector‑java to 8.0.28 – the bug also reproduces.

Thus the root cause is isolated to mysql‑connector‑java 8.0.28.

Confirming the Bug

GitHub commit history for the MySQL Connector/J project shows that version 8.0.28 unintentionally changed the auto‑commit handling, and later commits reverted it, labeling the change a bug.

Fix

Release 8.0.29 fixes the auto‑commit state issue when useLocalSessionState=true (Bug #106435, #33850099).

Upgrading the MySQL driver to 8.0.29 restores correct transaction rollback behavior.

Conclusion

The observed failure of Spring @Transactional rollback was caused by a bug in mysql‑connector‑java 8.0.28 that mishandles the auto‑commit flag when useLocalSessionState=true. Upgrading to 8.0.29 or disabling useLocalSessionState resolves the issue, preventing silent data loss in many Spring‑Boot applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

transactionmysqlConnector/Jautocommit
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.