'JPA @Version behavior when data is changed from unmanaged connection

Enabling @Version on table Customer when running the tests below

@Test
public void actionsTest1 () throws InterruptedException {
    CustomerState t = customerStateRepository.findById(1L).get();
    Thread.sleep(20000);
    t.setInvoiceNumber("1");
    customerStateRepository.save(t);
}

While actionsTest1 is sleeping, I run actionsTest2 which updates the invoice number to 2.

@Test
public void actionsTest2 () throws InterruptedException {
    CustomerState t = customerStateRepository.findById(1L).get();
    t.setInvoiceNumber("2");
    customerStateRepository.save(t);
}

When actionsTest1 returns from sleeping it tries to update too, and gets a ObjectOptimisticLockingFailureException

Works as expected.

But if I run actionsTest1 and while it is sleeping I open a SQL terminal and do a raw update of

update customer 
set invoice_number='3' where id=1

When actionsTest1 returns from sleeping, its versioning mechanism doesn't catch the case and updates the value back to 1.

Is that expected behavior? Does versioning work only with connections managed by JPA?



Solution 1:[1]

It works as expected. If you do a update manually, you have to update your version as well.

If you using JPA with @Version, JPA is incrementing the version column.

To get your expected result you have to write the statement like this

update customer set invoice_number='3', version=XYZ (mabye version+1) where id=1

Solution 2:[2]

Is that expected behavior?

Yes.

Does versioning work only with connections managed by JPA?

No, it also works when using any other way of updating your data. But everything updating the data has to adhere to the rules of optimistic locking:

  1. increment the version column whenever performing any update
  2. (only required when the other process also want to detect concurrent updates): on every update check that the version number hasn't changes since the data on which the update is based was loaded.

Solution 3:[3]

Hibernate automatically increases/changes the value in @Version mapped column in your database.

When you fetch an entity record, hibernate keeps a copy of the record of the data along with the value of @Version. While performing a merge or update operation, hibernate checks if the current value in of Version is still the same and matches the copy of entity fetched earlier. If the value matches, it means that the entity is not dirty(not updated by any other transaction) else an exception is thrown.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Philipp Schneider
Solution 2 Jens Schauder
Solution 3 Asgar