Ulrich Bestfleisch
Ulrich Bestfleisch

Reputation: 46

Hibernate: Entity changes in @PreUpdate callback are not persisted when lazy loading is active

We are using Hibernate 5.2.17 in our project and populate the last modification date within a @PreUpdate entity callback.

After a lengthy debugging session it seems like that when

  1. Bytecode enhancement for lazy loading is active
  2. Multiple entity fields are loaded lazily

the changes done to the entity within the callback are not persisted on database level.

I was about to file this as bug for Hibernate, but before that get feedback on whether we do something wrong.

Entity

@Entity(name = "Person")
private static class Person {

    @Id
    @GeneratedValue
    private int id;

    private String name;

    private Instant createdAt;

    private Instant lastUpdatedAt;

    @ElementCollection(fetch = FetchType.LAZY)
    private List<String> tags;

    @Lob
    @Basic(fetch = FetchType.LAZY)
    private ByteBuffer image;

    @PrePersist
    void beforeCreate() {
        createdAt = Instant.now();
    }

    @PreUpdate
    void beforeUpdate() {
        lastUpdatedAt = Instant.now();
    }
}

Test (abbreviated)

@Test
public void testPreUpdateModificationDate() throws Exception {
    EntityManager em = getOrCreateEntityManager();
    em.getTransaction().begin();
    Person p = new Person();
    em.persist(p);
    em.getTransaction().commit();

    int personId = p.id;
    em.refresh(p);

    p = em.find(Person.class, personId);
    assertNotNull(p);
    assertNotNull(p.createdAt);
    assertNull(p.lastUpdatedAt);

    p.name = "New Name";
    em.getTransaction().begin();
    em.merge(p);
    em.getTransaction().commit();
    assertEquals(p.name, "New Name");

    em.refresh(p);

    p = em.find(Person.class, personId);
    // the last line fails
    assertNotNull(p.lastUpdatedAt);
}

As soon as the one of the lazily loaded attributes is removed from Person, the test runs successfully.

In debugging it looks like the SQL generation for an entity update does not consider the fields changed in @PreUpdate as dirty. The class org.hibernate.persister.entity.AbstractPersister has a different SQL generation for "dynamic updates" which is chosen automatically when bytecode enhancement is active and more than 1 fetch group is present.

Upvotes: 1

Views: 986

Answers (2)

Ulrich Bestfleisch
Ulrich Bestfleisch

Reputation: 46

The issue has been fixed in Hibernate in 5.3.3.

Bug report: https://hibernate.atlassian.net/browse/HHH-12718

Upvotes: 1

K.Nicholas
K.Nicholas

Reputation: 11551

Works for me: not sure what is the issue, but you have many strange things. Perhaps start with this simple example and expand to your code to find the difference:

    tx.begin();
    Person p = new Person();
    em.persist(p);
    tx.commit();
    System.out.println(p);

    tx.begin();
    p.setName( "New Name" );
    tx.commit();
    System.out.println(p);

Upvotes: 0

Related Questions