Skip to main content

10.6 Isolation Levels

Isolation determines what concurrent transactions can observe when reading and writing the same data. dbVisitor's Isolation maps to JDBC Connection#setTransactionIsolation(int).

Isolation can be specified through all three transaction APIs:

Annotation-Based Transactions
@Transactional(isolation = Isolation.READ_COMMITTED)
public void createOrder(long orderId) {
...
}
Transaction Templates
txTemplate.execute(tranStatus -> {
...
return null;
}, Propagation.REQUIRED, Isolation.REPEATABLE_READ);
Programmatic Transactions
TransactionStatus tran = txManager.begin(
Propagation.REQUIRED,
Isolation.SERIALIZABLE
);

How to Choose

Desired EffectCommon Isolation Level
Use the database defaultDEFAULT
Avoid uncommitted reads while allowing concurrencyREAD_COMMITTED
Keep repeated reads of existing rows consistentREPEATABLE_READ
Require the strongest consistency and accept concurrency costsSERIALIZABLE
Explicitly allow dirty reads and weak consistencyREAD_UNCOMMITTED
info

Defaults and exact semantics differ by database. MySQL, PostgreSQL, and Oracle differ in support for REPEATABLE_READ and handling of phantom reads and locks. dbVisitor sets JDBC isolation but cannot change the database transaction model.

The examples use this table:

mysql> select * from students;
+----+-------+
| id | name |
+----+-------+
| 1 | Alice |
+----+-------+

DEFAULT

Use the default isolation level determined by the database driver.

  • Constant Isolation.DEFAULT
Use Default Isolation
@Transactional
public void createOrder(long orderId) {
...
}

Read Uncommitted

At the lowest isolation level, a transaction may read another transaction's uncommitted updates. If the writer rolls back, the reader has observed dirty data.

  • Constant Isolation.READ_UNCOMMITTED
Explicitly Allow READ_UNCOMMITTED
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public List<OrderInfo> queryFastButWeakConsistent() {
return orderMapper.queryRecentOrders();
}
TimeTransaction ATransaction BEffect
T1set isolation level read uncommitted
T2beginbegin
T3update students set name='bob' where id=1
T4select * from students where id=1Reads 'bob' (dirty read)
T5rollback
T6select * from students where id=1Reads 'Alice' again

Read Committed

Only committed changes from other transactions are visible. Repeated queries within one transaction may return different results if another transaction commits changes between them.

  • Constant Isolation.READ_COMMITTED
Use READ_COMMITTED for Business Writes
@Transactional(isolation = Isolation.READ_COMMITTED)
public void payOrder(long orderId) {
orderMapper.markPaid(orderId);
orderMapper.insertPayLog(orderId);
}
TimeTransaction ATransaction BEffect
T1set isolation level read committedset isolation level read committed
T2beginbegin
T3select * from students where id=1Reads 'Alice'
T4update students set name='bob' where id=1
T5select * from students where id=1Still reads 'Alice' (uncommitted changes invisible)
T6commit
T7select * from students where id=1Reads 'bob' (non-repeatable read)
T8commit

Repeatable Read

Protects repeated reads of existing rows against committed modifications by other transactions. Behavior for own writes, new rows in a range, and locking reads remains database-specific.

  • Constant Isolation.REPEATABLE_READ
Stable Reads Within One Transaction
@Transactional(isolation = Isolation.REPEATABLE_READ)
public OrderSummary buildOrderSummary(long orderId) {
Order order = orderMapper.queryOrder(orderId);
List<OrderItem> items = orderMapper.queryItems(orderId);
return new OrderSummary(order, items);
}
Phantom Read

Repeatable Read may allow phantom reads: Phantom reads usually mean rows appearing or disappearing from repeated range queries due to other commits. The example below shows how MySQL InnoDB consistent reads and updates may observe different data; this is not universal behavior at this level.

TimeTransaction ATransaction BEffect
T1set isolation level repeatable readset isolation level repeatable read
T2beginbegin
T3select * from students where id=99Empty result
T4insert into students (id, name) values (99, 'bob')
T5commit
T6select * from students where id=99Still empty (repeatable read)
T7update students set name='alice' where id=99Update succeeds
T8select * from students where id=99Data appears after the update

Serializable

Committed transactions must have an effect equivalent to some serial ordering; transactions do not have to run one at a time. This excludes dirty reads, non-repeatable reads, phantom reads, and non-serializable outcomes.

  • Constant Isolation.SERIALIZABLE
Use When the Strongest Consistency Is Required
@Transactional(isolation = Isolation.SERIALIZABLE)
public void allocateUniqueNumber(String bizType) {
Long nextNumber = numberMapper.queryNextNumber(bizType);
numberMapper.updateNextNumber(bizType, nextNumber + 1);
}
Performance Impact

Databases may implement this level with locks or concurrency-conflict detection; it does not mean a database-wide exclusive lock. Account for waits, deadlocks, or serialization failures. Retry the whole transaction when required by the database for a retryable conflict. See PostgreSQL Transaction Isolation

Isolation Comparison

The table describes phenomena allowed by the standard. Databases may provide stronger guarantees; for example, PostgreSQL Repeatable Read does not allow phantom reads.

Isolation LevelsDirty ReadNon-Repeatable ReadPhantom Read
READ_UNCOMMITTEDPossiblePossiblePossible
READ_COMMITTED-PossiblePossible
REPEATABLE_READ--Possible
SERIALIZABLE---

Further Reading