Understanding Transaction Isolation and Concurrency in JDBC
Course: Advanced Java Training
Topic: Understanding Transaction Isolation and Concurrency
Course Link: Advanced Java Training Course
Demo Link: Register for a Course Demo
JustAcademy Website: Visit JustAcademy
1. Introduction to Transaction Isolation and Concurrency
Database transaction isolation and concurrency are important concepts in Advanced Java development. A database application may serve many users simultaneously, with each user reading, inserting, updating, or deleting records. When multiple transactions access the same data, their operations can overlap and sometimes interfere with one another.
Concurrency refers to multiple transactions executing during overlapping periods. Transaction isolation determines how these transactions interact and what changes they can observe. Appropriate isolation and concurrency controls help applications maintain data consistency while supporting multiple users.
Java Database Connectivity (JDBC) provides transaction-management methods through the Connection interface. These include setAutoCommit(), setTransactionIsolation(), commit(), and rollback().
2. Learning Objectives
- Understand database transactions, concurrency, and isolation.
- Explain the ACID properties of transactions.
- Identify dirty reads, non-repeatable reads, phantom reads, and lost updates.
- Understand standard JDBC transaction isolation levels.
- Configure isolation levels through JDBC.
- Explore optimistic and pessimistic concurrency control.
- Understand database locking and deadlocks.
- Implement safer concurrent database updates using Java and SQL.
- Apply concurrency principles to real-world applications.
3. What Is Database Concurrency?
Database concurrency occurs when multiple transactions are processed at the same time or during overlapping periods. A database management system coordinates these transactions to preserve data integrity while allowing efficient use of database resources.
Example: Online Shopping Inventory
Suppose an online store has 10 units of a product. Two customers attempt to purchase 7 units each at nearly the same time.
- Transaction A reads the available stock as 10.
- Transaction B also reads the available stock as 10.
- Both transactions determine that 7 units appear to be available.
- If the application performs unprotected read-and-write operations, both orders may be accepted even though the available stock is insufficient.
A safe implementation must coordinate the operations so that stock cannot be oversold. Conditional SQL updates, transactions, suitable isolation, and database constraints can help enforce this business rule.
4. Why Is Transaction Isolation Important?
- Data consistency: Helps prevent transactions from relying on inappropriate intermediate states.
- Reliable updates: Reduces conflicting changes to shared records.
- Business rule enforcement: Helps maintain rules involving account balances, inventory, and order processing.
- Multi-user support: Allows multiple users to work with shared data safely.
- Predictable behavior: Establishes rules for reading and modifying data during concurrent execution.
- Application reliability: Works alongside database constraints, transactions, and validation logic.
5. Understanding ACID Properties
ACID describes four important properties associated with reliable database transactions.
| Property |
Meaning |
Example |
| Atomicity |
A transaction's operations are treated as one logical unit and are committed together or rolled back, subject to database capabilities. |
A bank transfer must not permanently debit an account without crediting the destination account. |
| Consistency |
A transaction preserves database constraints and application-defined rules. |
An account balance must follow the system's permitted balance rules. |
| Isolation |
Concurrent transactions interact according to the configured isolation and concurrency-control rules. |
One transaction should not incorrectly rely on another transaction's uncommitted changes. |
| Durability |
Committed changes are expected to persist according to the database's durability guarantees. |
A confirmed order remains stored after a normal application restart. |
6. Common Concurrency Problems
6.1 Dirty Read
A dirty read occurs when a transaction reads data written by another transaction before that data has been committed. If the writing transaction rolls back, the reading transaction has observed a value that never became committed database state.
Example: Transaction A changes an account balance from ₹5,000 to ₹2,000 but has not committed. Transaction B reads ₹2,000. If Transaction A rolls back, Transaction B has read an uncommitted value.
Prevention: Use an isolation level that prevents dirty reads, such as READ_COMMITTED, or a stronger supported level.
6.2 Non-Repeatable Read
A non-repeatable read occurs when a transaction reads the same row twice and obtains different committed values because another transaction updated and committed that row between the reads.
Example: Transaction A reads a product price of ₹500. Transaction B changes the price to ₹600 and commits. Transaction A reads the same row again and obtains ₹600.
Prevention: Use an isolation level that prevents non-repeatable reads, such as REPEATABLE_READ or SERIALIZABLE, according to database support.
6.3 Phantom Read
A phantom read occurs when a transaction repeats a query that selects rows matching a condition and receives a different set of rows because another transaction inserted or deleted matching records and committed.
Example: Transaction A queries employees earning more than ₹50,000 and receives 10 rows. Transaction B inserts an employee earning ₹60,000 and commits. When Transaction A repeats its query, it may receive 11 rows.
Prevention: Use an isolation strategy that prevents phantom reads under the applicable database isolation model, such as SERIALIZABLE.
6.4 Lost Update
A lost update can occur when two transactions read the same original value, calculate changes independently, and then overwrite each other's results.
Example: An account balance is ₹1,000. Transaction A reads the balance and adds ₹100. Transaction B reads the same balance and subtracts ₹50. If both write calculated absolute values without concurrency protection, one update may overwrite the other.
Prevention: Use atomic SQL updates, optimistic version checks, or appropriate locking. The exact protection depends on the database and transaction design.
6.5 Inconsistent Read
An inconsistent read occurs when a transaction reads related information at different points in time and obtains a combination that does not represent a consistent view for the intended operation.
Example: An application reads the debit side of a bank transfer and later reads the credit side after other transactions have changed related records. The combined values may not represent one consistent business state.
Prevention: Keep related reads within an appropriate transaction and select a suitable isolation or locking strategy.
7. JDBC Transaction Isolation Levels
JDBC defines standard transaction isolation constants in the Connection interface. Each level describes the minimum isolation behavior expected by the standard model. Supported levels and implementation details depend on the database and JDBC driver.
| Isolation Level |
Dirty Reads |
Non-Repeatable Reads |
Phantom Reads |
TRANSACTION_READ_UNCOMMITTED |
May occur |
May occur |
May occur |
TRANSACTION_READ_COMMITTED |
Prevented |
May occur |
May occur |
TRANSACTION_REPEATABLE_READ |
Prevented |
Prevented |
May occur under the standard model |
TRANSACTION_SERIALIZABLE |
Prevented |
Prevented |
Prevented under the standard model |
Important: Database engines may use locking, multiversion concurrency control (MVCC), or other mechanisms that affect observed behavior. Consult the database documentation and test the actual driver rather than assuming every database implements isolation identically.
7.1 READ_UNCOMMITTED
This is the weakest standard isolation level. It may allow transactions to read changes that other transactions have not committed.
- Dirty reads may occur.
- Non-repeatable reads and phantom reads may occur.
- It may allow greater concurrency in some database systems.
- It is generally unsuitable for business operations requiring dependable committed values.
7.2 READ_COMMITTED
This level prevents dirty reads. A transaction does not read uncommitted changes made by other transactions, but subsequent queries may observe newly committed changes.
- Prevents dirty reads.
- May permit non-repeatable reads.
- May permit phantom reads.
- Can be suitable for general application workloads that need committed data.
7.3 REPEATABLE_READ
This level prevents dirty reads and non-repeatable reads under the standard isolation model. Re-reading a row within the same transaction should not expose a different committed value for that row.
- Prevents dirty reads.
- Prevents non-repeatable reads.
- May permit phantom reads under the standard model.
- May introduce additional locking or concurrency costs depending on implementation.
7.4 SERIALIZABLE
This is the strictest standard JDBC isolation level. It aims to make concurrent transactions behave as though they were executed in a serial order.
- Prevents dirty reads, non-repeatable reads, and phantom reads under the standard model.
- Provides strong consistency for complex operations.
- May increase blocking, transaction retries, or serialization failures.
- Should be selected when its consistency benefits justify its performance costs.
8. Setting Transaction Isolation in JDBC
The Connection.setTransactionIsolation() method requests a supported isolation level for a JDBC connection.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class IsolationLevelExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/company";
String user = "root";
String password = "your_password";
try (Connection con =
DriverManager.getConnection(url, user, password)) {
con.setTransactionIsolation(
Connection.TRANSACTION_READ_COMMITTED
);
con.setAutoCommit(false);
System.out.println(
"Isolation level: " + con.getTransactionIsolation()
);
// Perform database operations here.
con.commit();
} catch (SQLException e) {
System.out.println("Database error: " + e.getMessage());
}
}
}
This example demonstrates isolation configuration. Production transaction code should also roll back after failures, handle rollback exceptions, and avoid reporting success if commit fails. Configure the isolation level before performing the transaction work that depends on it.
9. Checking Supported Isolation Levels
Not every database supports every isolation level in the same way. JDBC's DatabaseMetaData interface can be used to check whether the driver reports support for a standard isolation level.
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;
public class IsolationSupportExample {
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/company";
String user = "root";
String password = "your_password";
try (Connection con =
DriverManager.getConnection(url, user, password)) {
DatabaseMetaData meta = con.getMetaData();
System.out.println(
"READ_UNCOMMITTED: " +
meta.supportsTransactionIsolationLevel(
Connection.TRANSACTION_READ_UNCOMMITTED
)
);
System.out.println(
"READ_COMMITTED: " +
meta.supportsTransactionIsolationLevel(
Connection.TRANSACTION_READ_COMMITTED
)
);
System.out.println(
"REPEATABLE_READ: " +
meta.supportsTransactionIsolationLevel(
Connection.TRANSACTION_REPEATABLE_READ
)
);
System.out.println(
"SERIALIZABLE: " +
meta.supportsTransactionIsolationLevel(
Connection.TRANSACTION_SERIALIZABLE
)
);
}
}
}
Metadata is useful for discovering supported features, but database-specific documentation is still needed to understand the exact concurrency behavior.
10. Understanding Race Conditions
A race condition occurs when the result of a program depends on the timing or order of concurrent operations. In database applications, race conditions often arise when several transactions read and modify the same data.
Example: Unsafe Inventory Update
SELECT stock
FROM products
WHERE product_id = 10;
-- The application calculates a new stock value.
UPDATE products
SET stock = 3
WHERE product_id = 10;
This approach can overwrite another transaction's update because the application writes a value based on a previous read.
Safer Approach: Conditional Atomic Update
UPDATE products
SET stock = stock - ?
WHERE product_id = ?
AND stock >= ?;
Bind the quantity and product ID through a PreparedStatement, execute the update, and check the affected-row count. If one row is updated, the reduction was applied. If no row is updated, the product may not exist or the available stock may be insufficient. The database performs the condition check and update as one statement, reducing the risk of a read-modify-write race.
11. Optimistic and Pessimistic Concurrency Control
11.1 Optimistic Concurrency Control
Optimistic concurrency control assumes conflicts are relatively uncommon. The application reads a record and checks whether it has changed before saving an update. A version column is a common implementation technique.
Example Table
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
version INT NOT NULL DEFAULT 0
);
Version-Based Update
UPDATE products
SET price = ?,
version = version + 1
WHERE product_id = ?
AND version = ?;
The application binds the new price, product ID, and version originally read. If exactly one row is updated, the version check succeeded. If zero rows are updated, the record may have changed or no longer exist. The application can reload the record and decide whether to retry or report a conflict.
11.2 Pessimistic Concurrency Control
Pessimistic concurrency control uses locks to restrict conflicting operations while critical work is being performed. A database may provide locking reads to protect selected records within a transaction.
SELECT balance
FROM accounts
WHERE account_id = ?
FOR UPDATE;
This pattern can be useful when an application must read a value and make a related decision before updating it. The query should run within an appropriate transaction. Locking syntax, lock behavior, and support depend on the database engine.
12. Isolation Level Comparison
| Isolation Level |
Consistency Protection |
Concurrency Consideration |
| READ_UNCOMMITTED |
Lowest standard protection |
Dirty reads may be possible. |
| READ_COMMITTED |
Prevents dirty reads |
Repeated reads may return changed values. |
| REPEATABLE_READ |
Prevents dirty and non-repeatable reads under the standard model |
Phantom behavior depends on the applicable isolation model. |
| SERIALIZABLE |
Strongest standard isolation |
May introduce blocking, conflicts, or retries. |
The appropriate isolation level depends on business requirements, database capabilities, workload, and performance goals. A higher isolation level is not automatically the best choice for every application.
13. Deadlocks in Database Concurrency
A deadlock occurs when two or more transactions wait for resources held by one another, so none can proceed without database intervention.
Example
- Transaction A locks Account 1 and requests Account 2.
- Transaction B locks Account 2 and requests Account 1.
- Transaction A waits for Transaction B to release Account 2.
- Transaction B waits for Transaction A to release Account 1.
- The database detects the deadlock and typically aborts one transaction so the other can continue.
Reducing Deadlocks
- Access shared records in a consistent order across transactions.
- Keep transactions short.
- Avoid unnecessary database operations while holding locks.
- Use suitable indexes and efficient queries.
- Handle deadlock exceptions and retry eligible transactions safely.
- Use bounded retries rather than retrying continuously.
Deadlocks cannot always be prevented. Applications should recover safely when the database chooses a transaction as the deadlock victim.
14. Practical Example: Updating Inventory Safely
This example uses a conditional update to reduce inventory only when sufficient stock is available.
Sample Table
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
stock INT NOT NULL CHECK (stock >= 0)
);
INSERT INTO products
VALUES (10, 'Wireless Mouse', 10);
Java Program
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class InventoryConcurrencyExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/company";
String user = "root";
String password = "your_password";
int productId = 10;
int quantity = 3;
if (quantity <= 0) {
throw new IllegalArgumentException(
"Quantity must be greater than zero."
);
}
String sql =
"UPDATE products SET stock = stock - ? " +
"WHERE product_id = ? AND stock >= ?";
try (Connection con =
DriverManager.getConnection(url, user, password);
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, quantity);
ps.setInt(2, productId);
ps.setInt(3, quantity);
int rowsUpdated = ps.executeUpdate();
if (rowsUpdated == 1) {
System.out.println("Inventory updated successfully.");
} else {
System.out.println(
"Product not found or insufficient stock."
);
}
} catch (SQLException e) {
System.out.println(
"Inventory update failed: " + e.getMessage()
);
}
}
}
Explanation
- The program validates that the requested quantity is positive.
- The SQL statement checks the product ID and available stock.
- The database reduces stock only when sufficient stock is available.
- The affected-row count indicates whether the update succeeded.
- The database constraint provides an additional safeguard against negative stock.
For an order involving several products, place inventory updates and order records in one explicit transaction when all required changes must succeed together.
15. Practical Project: Multi-User Banking System
Project objective: Develop a Java application that supports multiple users performing deposits, withdrawals, and transfers while maintaining account consistency.
Project Requirements
- Store account information in a relational database.
- Use JDBC to connect to the database.
- Use explicit transactions for operations involving multiple related updates.
- Use conditional SQL updates to prevent invalid withdrawals.
- Use row locking or version-based updates where appropriate.
- Configure an isolation level according to consistency requirements.
- Handle SQL exceptions, deadlocks, and rollback failures.
- Record transaction history for auditing.
- Prevent duplicate transfers when requests are retried.
Suggested Implementation Steps
- Create tables for accounts and transaction history.
- Implement account balance retrieval.
- Implement deposits and withdrawals using safe SQL updates.
- Implement transfers using a single transaction.
- Add concurrency controls for simultaneous account updates.
- Test multiple users accessing the same accounts concurrently.
- Simulate insufficient balance, deadlocks, and failed updates.
- Verify that balances and transaction history remain consistent.
16. Testing Transaction Isolation and Concurrency
Concurrency problems can be difficult to reproduce because they depend on the timing of transactions. Tests should deliberately coordinate two or more database connections.
Testing Checklist
- Open two separate JDBC connections to the same database.
- Begin transactions on both connections where supported.
- Read or update the same record through both connections.
- Commit or roll back one transaction at a controlled point.
- Observe the results of subsequent reads and updates.
- Repeat the test using different supported isolation levels.
- Verify whether the database blocks, rejects, or allows each operation.
- Confirm that the application handles conflicts and exceptions correctly.
Use test data rather than production records. Results depend on the database engine, isolation level, query design, and concurrency-control implementation.
17. Common Mistakes and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Unexpected data changes between reads |
The selected isolation level permits non-repeatable reads or new matching rows. |
Review the isolation requirement and choose a suitable supported level. |
| Incorrect inventory quantity |
A read-modify-write sequence allows concurrent updates to conflict. |
Use atomic conditional updates or appropriate concurrency controls. |
| Transactions wait for a long time |
Locks are held by another transaction or a query is slow. |
Reduce transaction duration and inspect query plans and locks. |
| Deadlock exception |
Transactions acquired locks in conflicting orders. |
Use a consistent access order and safe, bounded retries. |
| Isolation level cannot be set |
The database or driver does not support the requested level or the change is attempted at an unsuitable time. |
Check metadata, driver documentation, and transaction state. |
| An update overwrites another user's changes |
The application writes a value calculated from stale data. |
Use version columns, conditional updates, or suitable locks. |
| An operation is repeated after a connection failure |
The original transaction outcome is uncertain. |
Use idempotency controls and reconcile the final database state before retrying. |
18. Best Practices for Transaction Isolation and Concurrency
- Choose an isolation level based on business consistency requirements.
- Check database and JDBC driver support before relying on a particular level.
- Keep transactions short and avoid unnecessary work while holding locks.
- Use
PreparedStatement for parameterized SQL.
- Prefer atomic SQL updates for operations such as balance changes and stock reductions.
- Use optimistic version checks when conflicts should be detected rather than blocked.
- Use locking reads when a workflow requires a protected read followed by a decision and update.
- Enforce critical rules with database constraints in addition to application validation.
- Handle deadlocks and transient failures with carefully bounded retry policies.
- Test concurrency scenarios using separate connections and realistic workloads.
- Monitor slow queries, lock waits, deadlocks, and transaction duration.
- Never assume every database implements isolation levels identically.
19. Practice Exercises
- Write a Java program that sets the isolation level to
TRANSACTION_READ_COMMITTED.
- Create two database connections and investigate how concurrent updates affect repeated reads.
- Demonstrate a dirty-read scenario if the database supports it.
- Compare
READ_COMMITTED and REPEATABLE_READ using a controlled test.
- Implement an inventory update that cannot reduce stock below zero.
- Add a version column to a table and implement optimistic concurrency control.
- Use a locking read to protect an account balance update.
- Simulate a deadlock in a test database and handle the resulting exception.
- Build a multi-user bank transfer program using JDBC transactions.
- Measure how isolation levels affect blocking and performance for a representative workload.
20. Interview Questions and Answers
Q1. What is transaction isolation?
Answer: Transaction isolation defines how concurrent transactions interact and what changes they can observe while executing.
Q2. What is database concurrency?
Answer: Database concurrency is the simultaneous or overlapping execution of transactions by multiple users or application processes.
Q3. What is a dirty read?
Answer: A dirty read occurs when a transaction reads changes made by another transaction before those changes are committed.
Q4. What is a non-repeatable read?
Answer: It occurs when a transaction reads the same row more than once and sees different committed values.
Q5. What is a phantom read?
Answer: A phantom read occurs when a repeated query returns a different set of rows because another transaction changes which records match the query condition.
Q6. Which JDBC isolation level is the strictest standard level?
Answer: Connection.TRANSACTION_SERIALIZABLE is the strictest standard JDBC isolation level.
Q7. How can an isolation level be set in JDBC?
Answer: Use Connection.setTransactionIsolation() with a supported isolation-level constant.
Q8. What is a lost update?
Answer: A lost update occurs when one transaction overwrites another transaction's change, often because both transactions read an old value and then write independently calculated results.
Q9. What is optimistic concurrency control?
Answer: It detects conflicting changes, often through a version column or conditional update, rather than relying entirely on locks to prevent conflicts in advance.
Q10. What is pessimistic concurrency control?
Answer: It uses locks to restrict conflicting operations while a transaction performs critical work.
Q11. What is a deadlock?
Answer: A deadlock occurs when transactions wait for resources held by each other so that none can proceed until the database intervenes.
Q12. Does SERIALIZABLE always provide the best performance?
Answer: No. It provides strong standard isolation guarantees but may introduce additional blocking, retries, or resource usage. The best level depends on the application.
21. Quick Revision Checklist
- Concurrency means transactions overlap in execution.
- Isolation controls interactions between concurrent transactions.
- ACID stands for Atomicity, Consistency, Isolation, and Durability.
- Dirty reads involve uncommitted data.
- Non-repeatable reads involve changed values for the same row.
- Phantom reads involve changes to the set of matching rows.
setTransactionIsolation() configures a supported JDBC isolation level.
- Atomic SQL updates help prevent common read-modify-write races.
- Optimistic concurrency control detects conflicting updates.
- Pessimistic concurrency control uses locks to protect critical work.
- Deadlocks require safe recovery and sometimes bounded retries.
- Database-specific behavior must be verified through testing and documentation.
22. Conclusion
Understanding transaction isolation and concurrency is essential for building reliable Java applications that work with shared database records. By learning standard JDBC isolation levels, recognizing common concurrency anomalies, using atomic SQL updates, and applying locking or version-based controls, developers can reduce inconsistent data and handle simultaneous operations more safely.
For further learning, explore the Advanced Java Training Course, register for a course demo, or visit the JustAcademy website.
23. Additional Learning Resources