6

In my application I defined following classes:

@Entity
@Table(name = "forums")
public class Forum {
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;

    private String name;
    private Date lastActivity;

    @OneToMany(mappedBy = "forum", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE })
    private List<Post> posts;

@Entity
@Table(name = "posts")
public class Post {
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;

    private String username;
    private String content;
    private Date creationDate;

    @ManyToOne(optional = false, cascade = { CascadeType.MERGE, CascadeType.PERSIST })
    private Forum forum;

    public Post() {
        creationDate = new Date();
    }

    @PrePersist
    private void onPersist() {
        System.out.println(getClass().getName() + ": onPersist");
        if (creationDate == null) {
            creationDate = new Date();
        }

        forum.setLastActivity(creationDate);
    }

    @PreUpdate
    private void onUpdate() {
        forum.setLastActivity(new Date());
    }

If I try adding new posts to forum entity, lastActivity field is correctly updated in database by @PrePersist callback. But if I try to update post entity using following code:

  entityManager.getTransaction().begin();
  Post post = entityManager.find(Post.class, "postId");
  post.setContent("New post text");
  entityManager.merge(post);
  entityManager.getTransaction().commit();

only post data are updated and lastActivity field value doesn't change. In my opinion @PreUpdate method should do the trick and update Forum entity. Is this a bug or am I missing something?

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
lhanusiak
  • 63
  • 1
  • 4

2 Answers2

12

It is not bug, even that with a fast try this worked for me way you expected. Negative news is that it is not guaranteed to work, because of:

From page 93 in JPA 2.0 specification:

In general, the lifecycle method of a portable application should not invoke EntityManager or Query operations, access other entity instances, or modify relationships within the same persistence context.[43] A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.

And page 95:

It is implementation-dependent as to whether callback methods are invoked before or after the cascading of the lifecycle events to related entities. Applications should not depend on this ordering.

Mikko Maunu
  • 41,366
  • 10
  • 132
  • 135
  • This isn’t a very good news for me but I really appreciate your help. Do you know any work around for this problem or maybe you can tell me what implementation did you used to test my code? Thank you in advance for your reply. – lhanusiak Oct 13 '11 at 14:28
  • Hibernate 3.6.2, HSQL. Not in server but as Java SE, resource local transactions. – Mikko Maunu Oct 13 '11 at 14:52
  • 1
    I strongly recommend on not relying on this, as @MikkoMaunu suggests. `@PreUpdate` did not work for me when updating a field in a one-to-many child entity using Hibernate 4.1.5. – Stefan Haberl Jan 21 '13 at 16:21
0

post.merge() has no use here. post is clearly attached to the session already.

Make sure you've got the content attribute mapped to a column, if it is not, Hibernate has no way of telling if the entity is dirty, and therefore flush the changes to the DB.

Xavi López
  • 27,550
  • 11
  • 97
  • 161
  • Indeed post.merge() is unnecessary in this case. Execution of mentioned code changes DB value of post.content field. My problem is that post.forum.lastActivity field isn't updated. – lhanusiak Oct 13 '11 at 14:15