Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java JDBC isolation: uncommitted writes and separate sessions

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

Transaction isolation determines which changes a database session can observe and how concurrent operations interact; it is a database contract, not just a Java enum.

Download Java source kit

Java 8+. The downloadable Maven fixture supplies H2 2.3.232.

Use two physical sessions

The example opens writer and reader connections to the same local H2 database. The writer updates a quantity without committing. Under READ_COMMITTED, the reader’s ordinary query observes the committed value, then observes the new value after the writer commits.

Two logical calls through one connection would not prove this boundary because a transaction ordinarily sees its own writes. The fixture therefore owns two connections and sets their transaction modes explicitly. It also rolls back in cleanup if an uncommitted writer path remains.

Isolation names do not define every engine’s locking, snapshot or retry behavior. The verified output belongs to H2 2.3.232 under this workload. PostgreSQL, MySQL and another engine need their own integration tests before the application relies on a specific interleaving.

Design the complete read-modify-write rule

An isolation setting alone does not make a stock reservation correct. Decide whether the operation uses a locked row, a conditional update with a checked row count, or a versioned optimistic update. Handle deadlock or serialization failure with a bounded whole-transaction retry policy where that engine requires it.

Working program

Java
import java.sql.*;
public class ReceiptIsolationSessions {
    static int read(Connection connection)throws SQLException{
        try(PreparedStatement query=connection.prepareStatement("SELECT units FROM stock WHERE id=1");ResultSet rows=query.executeQuery()){if(!rows.next())throw new SQLException("Missing stock");return rows.getInt(1);}
    }
    public static void main(String[] args)throws Exception{
        String url="jdbc:h2:mem:isolation_"+java.util.UUID.randomUUID();
        try(Connection writer=DriverManager.getConnection(url);Connection reader=DriverManager.getConnection(url)){
            try(Statement schema=writer.createStatement()){schema.execute("CREATE TABLE stock(id INT PRIMARY KEY, units INT)");schema.executeUpdate("INSERT INTO stock VALUES(1,7)");}
            reader.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);writer.setAutoCommit(false);
            try{
                try(PreparedStatement update=writer.prepareStatement("UPDATE stock SET units=? WHERE id=1")){update.setInt(1,11);if(update.executeUpdate()!=1)throw new SQLException("Missing update");}
                System.out.println(read(reader));writer.commit();System.out.println(read(reader));
            }finally{writer.rollback();}
        }
    }
}

Output

Output
7
11

Costs and boundaries

This query touches one primary-key row. Real cost includes database scheduling, locking, I/O and round trips. The fixture verifies one visibility case in H2; it does not establish phantom-read, write-skew or distributed-transaction behavior for another database.

Common Mistakes

  • One connection is not evidence of visibility across two sessions.
  • The same isolation name can have engine-specific details.
  • A retry must replay a complete valid transaction boundary.

Read next

Commit and rollback, Repository invariant.

Apply this contract in Spring

Spring TransactionTemplate: roll back a failed multi-row change. These lessons keep framework assembly separate from the Java contract.

Continue with checked relay behavior

Continue with Spring consumer idempotency: reserve an event ID with the stock mutation.

java
database-isolation
Storage details