JOOQ中结合POJO实现乐观锁及解决映射后update()抛出DataChangedException问题
Hey Alexander, let's work through this problem you're facing with jOOQ, POJOs, and optimistic locking. The root issue here is that when you create a new UserRecord from a POJO, jOOQ doesn't populate the internal originals[] array—this array holds the record's state as it was loaded from the database, which is what jOOQ uses to validate optimistic lock checks in update() or store().
Here are a few solid solutions to fix this:
1. Reuse the Original Record (Simplest Approach)
Instead of creating a brand new UserRecord from your modified POJO, merge the POJO's changes back into the original record you fetched from the database. This preserves the originals[] array that jOOQ needs for optimistic locking:
// Fetch the original record with intact originals state UserRecord userRecord = context.fetchOne(...); UserPojo userPojo = mapper.toPojo(userRecord); // Modify the POJO userPojo.setName("new_name"); // Merge POJO changes back into the original record mapper.toRecord(userPojo, userRecord); // Now store() will trigger optimistic lock checks correctly userRecord.store();
This works because the original userRecord still has its originals[] field populated from the initial fetch. The store() method will compare the current values (including the version field) against the original state and the database, handling optimistic lock validation as expected.
2. Manually Populate Original Values for New Records
If you absolutely need to create a new UserRecord from the POJO, you can manually set the original optimistic lock field value(s) so jOOQ can perform its check:
UserRecord userRecord = context.fetchOne(...); UserPojo userPojo = mapper.toPojo(userRecord); userPojo.setName("new_name"); UserRecord userRecordForSave = context.newRecord(USER, userPojo); // Set the original version value from the fetched record userRecordForSave.setOriginal(USER.VERSION, userRecord.getVersion()); // Mark the version field as unchanged (so jOOQ doesn't treat it as a modified value) userRecordForSave.changed(USER.VERSION, false); // Now update() will validate the optimistic lock correctly userRecordForSave.update();
If you're using jOOQ's built-in @Version annotation for optimistic locking, this ensures that jOOQ can compare the original version against the database's current value.
3. Batch Set Original Values (For Multiple Fields)
If you need to preserve original values for more than just the version field, you can copy all original values from the fetched record to the new one:
UserRecord userRecord = context.fetchOne(...); UserPojo userPojo = mapper.toPojo(userRecord); userPojo.setName("new_name"); UserRecord userRecordForSave = context.newRecord(USER, userPojo); // Copy all original values from the fetched record userRecordForSave.setOriginal(userRecord.originalValues()); // Mark only the fields you modified as changed (e.g., NAME) userRecordForSave.changed(USER.NAME, true); // Mark other fields (like VERSION) as unchanged userRecordForSave.changed(USER.VERSION, false); userRecordForSave.update();
Key Takeaway
jOOQ's optimistic locking relies on the originals[] array to track the record's state at load time. By either reusing the original record or manually populating the original values in the new record, you can maintain this state while still using your POJO mapping logic.
内容的提问来源于stack exchange,提问作者Alexander Golub

