forked from iluwatar/java-design-patterns
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
optimistic-offline-lock pattern added (issue iluwatar#1306)
- Loading branch information
1 parent
db4fbb3
commit fb79039
Showing
13 changed files
with
1,228 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
--- | ||
layout: pattern | ||
title: Optimistic Offline Lock | ||
folder: optimistic-offline-lock | ||
permalink: /patterns/optimistic-offline-lock/ | ||
categories: Concurrency | ||
language: en | ||
tags: | ||
- Performance | ||
--- | ||
|
||
## Intent | ||
Optimistic Offline Lock pattern helps with data integrity when there is risk of concurrent | ||
actions on the same record of data. | ||
|
||
## Explanation | ||
Often a business transaction executes across a series of system transactions. Once outside the | ||
confines of a single system transaction, we can’t depend on our database manager alone to ensure | ||
that the business transaction will leave the record data in a consistent state. Data integrity is | ||
at risk once two sessions begin to work on the same records and lost updates are quite possible. | ||
|
||
Optimistic Offline Lock solves this problem by validating that the changes about to be committed | ||
by one session don’t conflict with the changes of another session. A successful pre-commit validation | ||
is, in a sense, obtaining a lock indicating it’s okay to go ahead with the changes to the record data. | ||
So long as the validation and the updates occur within a single system transaction the business | ||
transaction will display consistency. | ||
|
||
**Programmatic Example** | ||
|
||
The code below shows example of delete operation which check if session's copy of version is the | ||
same as the version in DB. The transaction is committed only when the versions match, an | ||
OptimisticLockException is thrown otherwise. | ||
|
||
```java | ||
@Override | ||
public void delete(Customer customer) { | ||
Connection connection = null; | ||
PreparedStatement preparedStatement = null; | ||
try { | ||
connection = getConnection(); | ||
preparedStatement = connection.prepareStatement("DELETE FROM CUSTOMERS WHERE id = ? AND version = ?"); | ||
preparedStatement.setLong(1, customer.getId()); | ||
preparedStatement.setInt(2, customer.getVersion()); | ||
if (preparedStatement.executeUpdate() == 0) { | ||
throwOptimisticLockException(customer); | ||
} | ||
} catch (SQLException exception) { | ||
LOGGER.error(exception.getMessage(), exception); | ||
} finally { | ||
closeDbResources(preparedStatement, connection); | ||
} | ||
} | ||
``` | ||
|
||
## Class diagram | ||
![alt text](./etc/optimistic-offline-lock.urm.png "Optimistic Offline Lock") | ||
|
||
## Applicability | ||
Use the Optimistic Offline Lock pattern when the chance of conflict between any two business transactions is low. | ||
|
||
## Known uses | ||
* source code management (SCM) | ||
|
||
## Credits | ||
* [Optimistic Offline Lock](https://books.google.fi/books?id=vqTfNFDzzdIC&pg=PA416#v=onepage&q&f=false) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions
72
optimistic-offline-lock/etc/optimistic-offline-lock.urm.puml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
@startuml | ||
package com.iluwatar.optimistic.offline.lock.customer { | ||
class Customer { | ||
- firstName : String | ||
- id : long | ||
- lastName : String | ||
- modified : Timestamp | ||
- modifiedBy : String | ||
- serialVersionUID : long {static} | ||
- version : int | ||
+ Customer(id : long, firstName : String, lastName : String) | ||
~ Customer(id : long, firstName : String, lastName : String, modifiedBy : String, modified : Timestamp, version : int) | ||
+ getFirstName() : String | ||
+ getId() : long | ||
+ getLastName() : String | ||
+ getModified() : Timestamp | ||
+ getModifiedBy() : String | ||
+ getVersion() : int | ||
+ setFirstName(firstName : String) | ||
+ setLastName(lastName : String) | ||
+ toString() : String | ||
} | ||
interface CustomerDataMapper { | ||
+ delete(Customer) {abstract} | ||
+ find(long) : Customer {abstract} | ||
+ getAllCustomers() : List<Customer> {abstract} | ||
+ insert(Customer, String) {abstract} | ||
+ update(Customer, String) {abstract} | ||
} | ||
class CustomerDataMapperImpl { | ||
- LOGGER : Logger {static} | ||
- dataSource : DataSource | ||
+ CustomerDataMapperImpl(dataSource : DataSource) | ||
- closeDbResources(preparedStatement : PreparedStatement, connection : Connection) | ||
- closeDbResources(resultSet : ResultSet, preparedStatement : PreparedStatement, connection : Connection) | ||
- createCustomer(resultSet : ResultSet) : Customer | ||
+ delete(customer : Customer) | ||
+ find(id : long) : Customer | ||
+ getAllCustomers() : List<Customer> | ||
- getConnection() : Connection | ||
+ insert(customer : Customer, modifiedBy : String) | ||
- throwConcurrencyException(customer : Customer) | ||
+ update(customer : Customer, modifiedBy : String) | ||
} | ||
class CustomerSchemaSql { | ||
+ CREATE_SCHEMA_SQL : String {static} | ||
+ DELETE_SCHEMA_SQL : String {static} | ||
+ CustomerSchemaSql() | ||
} | ||
} | ||
package com.iluwatar.optimistic.offline.lock { | ||
class App { | ||
- DB_URL : String {static} | ||
- LOGGER : Logger {static} | ||
+ App() | ||
- closeDbResources(statement : Statement, connection : Connection) {static} | ||
- createDataSource() : DataSource {static} | ||
- createSchema(dataSource : DataSource) {static} | ||
- deleteCustomer(customerDataMapper : CustomerDataMapper, id : long, sleepTime : long) {static} | ||
- deleteSchema(dataSource : DataSource) {static} | ||
- deleteUpdatedCustomerConcurrencyExample(customerDataMapper : CustomerDataMapper) {static} | ||
- generateSampleCustomers(customerDataMapper : CustomerDataMapper) {static} | ||
+ main(args : String[]) {static} | ||
- showAllCustomers(customerDataMapper : CustomerDataMapper) {static} | ||
- updateCustomer(customerDataMapper : CustomerDataMapper, id : long, setFirstName : String, setLastName : String, sleepTime : long) {static} | ||
- updateCustomerConcurrencyExample(customerDataMapper : CustomerDataMapper) {static} | ||
- updateDeletedCustomerConcurrencyExample(customerDataMapper : CustomerDataMapper) {static} | ||
- waitForThreadsToFinishExecution(executorService : ExecutorService) {static} | ||
} | ||
} | ||
CustomerDataMapperImpl ..|> CustomerDataMapper | ||
@enduml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
The MIT License | ||
Copyright © 2014-2021 Ilkka Seppälä | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
--> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>java-design-patterns</artifactId> | ||
<groupId>com.iluwatar</groupId> | ||
<version>1.26.0-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
<artifactId>optimistic-offline-lock</artifactId> | ||
<dependencies> | ||
<dependency> | ||
<groupId>com.h2database</groupId> | ||
<artifactId>h2</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.junit.jupiter</groupId> | ||
<artifactId>junit-jupiter</artifactId> | ||
<version>RELEASE</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-assembly-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<configuration> | ||
<archive> | ||
<manifest> | ||
<mainClass>com.iluwatar.optimistic.offline.lock.App</mainClass> | ||
</manifest> | ||
</archive> | ||
</configuration> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
Oops, something went wrong.