1. Understanding Optimistic Locking
Optimistic locking assumes that multiple transactions can complete without interfering with each other. Rather than locking a record when reading it, optimistic locking checks if a record has been modified before committing any changes. If the data has been modified by another transaction, the current transaction is rolled back, and an exception is thrown.
1.1 What is Optimistic Locking?
Optimistic locking is a mechanism used to avoid conflicts by assuming that multiple transactions will rarely update the same data simultaneously. The strategy involves:
- Versioning: Each row of the table has a version number or timestamp.
- Validation: Before updating, the application checks if the version number or timestamp has changed.
- Update: If the data is unchanged, the update proceeds, and the version number is incremented.
1.2 Why Use Optimistic Locking?
Optimistic locking is beneficial when:
- Low Contention: When conflicts between transactions are rare.
- High Performance: It avoids the overhead of locking resources, which is beneficial in high-throughput systems.
- Scalability: It supports scalability by allowing multiple transactions to work on the same data simultaneously without the need for locks.
2. Implementing Optimistic Locking in JDBC
In this section, we'll walk through a practical example of implementing optimistic locking in JDBC using versioning.
2.1 Setting Up the Database Schema
First, let's define a database schema with a version column. We'll use a Product table as an example:
CREATE TABLE Product (
id INT PRIMARY KEY,
name VARCHAR(50),
price DECIMAL(10, 2),
version INT
);
2.2 Implementing the JDBC Code
Next, we'll implement JDBC code to update the product's price, ensuring that optimistic locking is respected.
public void updateProductPrice(int productId, BigDecimal newPrice, int currentVersion) throws SQLException {
String updateQuery = "UPDATE Product SET price = ?, version = version + 1 WHERE id = ? AND version = ?";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
PreparedStatement pstmt = conn.prepareStatement(updateQuery)) {
pstmt.setBigDecimal(1, newPrice);
pstmt.setInt(2, productId);
pstmt.setInt(3, currentVersion);
int rowsAffected = pstmt.executeUpdate();
if (rowsAffected == 0) {
throw new OptimisticLockException("The product was updated by another transaction.");
}
}
}
2.3 Handling the OptimisticLockException
The OptimisticLockException
is thrown when the update fails due to the version mismatch. Here's how you might handle this in your application:
try {
updateProductPrice(productId, newPrice, currentVersion);
} catch (OptimisticLockException e) {
System.out.println("Update failed: " + e.getMessage());
}
2.4 Demo Result
In a demo scenario where two users try to update the same product simultaneously, one of the transactions will succeed, and the other will throw an OptimisticLockException
. This ensures that no updates are lost, and data integrity is maintained.
3. Best Practices for Optimistic Locking
Optimistic locking can be highly effective when implemented correctly. Here are some best practices to follow: