同一事务内变更的可见性问题及JDBC与Spring场景差异
Great question—this is such a common gotcha when moving between raw JDBC and Spring's managed transactions. Let’s unpack what’s happening here, step by step.
Core Rule: Same Transaction = Changes Are Visible
First things first: Within a single transaction (same database connection), all your insert/update changes should be immediately visible to subsequent select statements in that same transaction. This is a fundamental part of ACID compliance—your transaction should see its own state consistently, regardless of isolation level (isolation levels only govern visibility between different concurrent transactions, not your own).
Why Raw JDBC Might Seem to "Hide" Changes
If you’re not seeing your uncommitted changes in raw JDBC, the most likely culprit is not using the same Connection object for all operations in the transaction. Transactions are strictly bound to a single Connection in JDBC. If your insert/update uses one connection, and your subsequent select grabs a new one from the pool, that new connection won’t see the uncommitted changes (since they’re tied to the original connection’s transaction).
Double-check your raw JDBC code—make sure you’re reusing the same Connection instance for every statement in the transaction. For example, this code will see the inserted data:
// Get a single connection for the entire transaction Connection conn = DriverManager.getConnection(dbUrl, user, password); conn.setAutoCommit(false); try { // Insert data PreparedStatement insertStmt = conn.prepareStatement("INSERT INTO users(id, name) VALUES (?, ?)"); insertStmt.setInt(1, 123); insertStmt.setString(2, "Alice"); insertStmt.executeUpdate(); // Select the same data using the SAME connection PreparedStatement selectStmt = conn.prepareStatement("SELECT name FROM users WHERE id = ?"); selectStmt.setInt(1, 123); ResultSet rs = selectStmt.executeQuery(); if (rs.next()) { System.out.println(rs.getString("name")); // Prints "Alice" immediately } conn.commit(); } catch (SQLException e) { conn.rollback(); } finally { conn.close(); }
If your code is creating a new Connection for the select, that’s why you’re not seeing the changes.
Why Spring JdbcTemplate Works As Expected
Spring’s DataSourceTransactionManager solves this problem automatically by binding the transactional Connection to the current thread (using a ThreadLocal). Every time you use JdbcTemplate within the transaction context, it retrieves the pre-bound connection instead of grabbing a new one from the pool. This ensures all operations in the transaction share the same connection, so your uncommitted changes are visible to subsequent selects.
Spring handles all the connection management behind the scenes, so you don’t have to manually track or reuse connections—this is one of the key benefits of using Spring’s transaction abstraction.
Vendor/Driver Dependencies?
The rule of same-connection transaction visibility is part of the JDBC spec’s implicit behavior, so it should hold across all compliant drivers and databases. For your setup (mysql-connector-java 8.0.11, JDBC 4.2, Java 8), MySQL’s driver fully adheres to this—there’s no special configuration that would break this behavior. The only edge cases would be if you’re using a database with non-standard transaction semantics, but MySQL isn’t one of them.
Quick Recap
- Same connection = same transaction = changes are visible: This is the baseline rule.
- Raw JDBC issue: Almost always a connection reuse problem.
- Spring’s magic: Thread-bound connections ensure all transactional operations share the same session.
内容的提问来源于stack exchange,提问作者Andreas Gelever

