Available in versions: Dev (3.19) | Latest (3.18) | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11 | 3.10 | 3.9
Optimistic locking
Applies to ✅ Open Source Edition ✅ Express Edition ✅ Professional Edition ✅ Enterprise Edition
jOOQ allows you to perform CRUD operations using optimistic locking. You can immediately take advantage of this feature by activating the relevant executeWithOptimisticLocking Setting. Without any further knowledge of the underlying data semantics, this will have the following impact on store() and delete() methods:
- INSERT statements are not affected by this Setting flag
- Prior to UPDATE or DELETE statements, jOOQ will run a SELECT .. FOR UPDATE statement, pessimistically locking the record for the subsequent UPDATE / DELETE
- The data fetched with the previous SELECT will be compared against the data in the record being stored or deleted
- An
org.jooq.exception.DataChangedException
is thrown if the record had been modified or deleted in the meantime, or if optimistic locking is performed on an unversioned record that hasn't been fetched from the database. - The record is successfully stored / deleted, if the record had not been modified in the mean time.
The above changes to jOOQ's behaviour are transparent to the API, the only thing you need to do for it to be activated is to set the Settings flag. Here is an example illustrating optimistic locking:
// Properly configure the DSLContext DSLContext optimistic = DSL.using(connection, SQLDialect.ORACLE, new Settings().withExecuteWithOptimisticLocking(true)); // Fetch a book two times BookRecord book1 = optimistic.fetchOne(BOOK, BOOK.ID.eq(5)); BookRecord book2 = optimistic.fetchOne(BOOK, BOOK.ID.eq(5)); // Change the title and store this book. The underlying database record has not been modified, it can be safely updated. book1.setTitle("Animal Farm"); book1.store(); // Book2 still references the original TITLE value, but the database holds a new value from book1.store(). // This store() will thus fail: book2.setTitle("1984"); book2.store();
Optimised optimistic locking using TIMESTAMP fields
If you're using jOOQ's code generator, you can take indicate TIMESTAMP or UPDATE COUNTER fields for every generated table in the code generation configuration. Let's say we have this table:
CREATE TABLE book ( -- This column indicates when each book record was modified for the last time MODIFIED TIMESTAMP NOT NULL, -- [...] )
The MODIFIED column will contain a timestamp indicating the last modification timestamp for any book in the BOOK table. If you're using jOOQ and it's store() methods on UpdatableRecords, jOOQ will then generate this TIMESTAMP value for you, automatically. However, instead of running an additional SELECT .. FOR UPDATE statement prior to an UPDATE or DELETE statement, jOOQ adds a WHERE-clause to the UPDATE or DELETE statement, checking for TIMESTAMP's integrity. This can be best illustrated with an example:
// Properly configure the DSLContext DSLContext optimistic = DSL.using(connection, SQLDialect.ORACLE, new Settings().withExecuteWithOptimisticLocking(true)); // Fetch a book two times BookRecord book1 = optimistic.fetchOne(BOOK, BOOK.ID.eq(5)); BookRecord book2 = optimistic.fetchOne(BOOK, BOOK.ID.eq(5)); // Change the title and store this book. The MODIFIED value has not been changed since the book was fetched. // It can be safely updated book1.setTitle("Animal Farm"); book1.store(); // Book2 still references the original MODIFIED value, but the database holds a new value from book1.store(). // This store() will thus fail: book2.setTitle("1984"); book2.store();
As before, without the added TIMESTAMP column, optimistic locking is transparent to the API.
Optimised optimistic locking using VERSION fields
Instead of using TIMESTAMPs, you may also use numeric VERSION fields, containing version numbers that are incremented by jOOQ upon store() calls.
Note, for explicit pessimistic locking, please consider the manual's section about the FOR UPDATE clause. For more details about how to configure TIMESTAMP or VERSION fields, consider the manual's section about advanced code generator configuration.
References to this page
Feedback
Do you have any feedback about this page? We'd love to hear it!