Search code examples
hibernatejpajava-timeoptimistic-lockingstaleobjectstateexception

Using LocalDateTime as @Version field with Hibernate 5.3 / JPA 2.2 on Java 11 leads to StaleObjectStateException


Consider an entity called Template with a field:

@Version
@Column(name = "LAST_UPDATE", nullable = false)
private LocalDateTime lastUpdate; // Java 8's class

The backing Oracle database field is TIMESTAMP.

Now executing the following code:

        // get EntityManager, start transaction
        final var t = new Template();
        t.setName("new template");
        t.setSubject("subject");

        em.persist(t);
        t.setSubject("subject - mod");
        tx.commit();

Causes an exception to be thrown:

Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [Template#169]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2525)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3357)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3231)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3632)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:146)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1453)
... 59 common frames omitted

If I change the type of lastUpdate to java.sql.Timestamp, the code works properly. What could be the problem?

Although not stated exactly in the docs, i think Hibernate supports this type as @Version field.

A test case is available here: https://github.com/imeszaros/jooby-hbm-localdatetime


Solution

  • Just for the record: Oracle's TIMESTAMP type has a precision of 6 by default, which can only store milliseconds.

    Java's LocalDateTime however may have nanoseconds, therefore you need to use TIMESTAMP(9) in the database to be able to store the values without the loss of precision.