How to use @PrePersist and @PreUpdate on Embeddable with JPA and Hibernate

(Last Updated On: May 2, 2018)

Introduction

In a previous article, I explained how you could audit entity modifications using the JPA @EntityListeners for embeddable types.

Since Hibernate ORM 5.2.17 now allows you to use the @PrePersist and @PreUpdate JPA entity listeners, we can simplify the previous example, as you will see in this article.

Domain Model

Assuming we have the following Domain Model classes:

We want to encapsulate the audit logic in the Audit embeddable type:

@Embeddable
public class Audit {

    @Column(name = "created_on")
    private LocalDateTime createdOn;

    @Column(name = "created_by")
    private String createdBy;
    
    @Column(name = "updated_on")
    private LocalDateTime updatedOn;

    @Column(name = "updated_by")
    private String updatedBy;

    @PrePersist
    public void prePersist() {
        createdOn = LocalDateTime.now();
        createdBy = LoggedUser.get();
    }

    @PreUpdate
    public void preUpdate() {
        updatedOn = LocalDateTime.now();
        updatedBy = LoggedUser.get();
    }

    //Getters and setters omitted for brevity
}

Notice the prePersist and preUpdate methods which are annotated with the JPA entity event listeners.

The JPA entities will use the Audit embeddable type as follows:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;
    
    @Embedded
    private Audit audit = new Audit();

    @ManyToMany
    @JoinTable(
        name = "post_tag",
        joinColumns = @JoinColumn(name = "post_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private List<Tag> tags = new ArrayList<>();

    //Getters and setters omitted for brevity
}

@Entity(name = "Tag")
@Table(name = "tag")
public class Tag {

    @Id
    private String name;

    @Embedded
    private Audit audit = new Audit();

    //Getters and setters omitted for brevity
}

Testing time

Now, when inserting 3 Tag entities:

Tag jdbc = new Tag();
jdbc.setName("JDBC");
entityManager.persist(jdbc);

Tag hibernate = new Tag();
hibernate.setName("Hibernate");
entityManager.persist(hibernate);

Tag jOOQ = new Tag();
jOOQ.setName("jOOQ");
entityManager.persist(jOOQ);

Hibernate properly sets the created_on and created_by columns on the associated tag rows:

INSERT INTO tag (
    created_by, 
    created_on, 
    updated_by, 
    updated_on, 
    name
) 
VALUES (
    'Alice', 
    '2018-05-02 09:56:54.939', 
    NULL(VARCHAR), 
    NULL(TIMESTAMP), 
    'JDBC'
)

INSERT INTO tag (
    created_by, 
    created_on, 
    updated_by, 
    updated_on, 
    name
) 
VALUES (
    'Alice', 
    '2018-05-02 09:56:54.955', 
    NULL(VARCHAR), 
    NULL(TIMESTAMP), 
    'Hibernate'
)

INSERT INTO tag (
    created_by, 
    created_on, 
    updated_by, 
    updated_on, 
    name
) 
VALUES (
    'Alice', 
    '2018-05-02 09:56:54.955', 
    NULL(VARCHAR), 
    NULL(TIMESTAMP), 
    'jOOQ'
)

The same goes for the Post entity:

Post post = new Post();
post.setId(1L);
post.setTitle(
    "High-Performance Java Persistence, 1st Edition
");

post.getTags().add(
    entityManager.find(Tag.class, "JDBC")
);
post.getTags().add(
    entityManager.find(Tag.class, "Hibernate")
);
post.getTags().add(
    entityManager.find(Tag.class, "jOOQ")
);

entityManager.persist(post);

Hibernate generating the following INSERT statements:

INSERT INTO post (
    created_by, 
    created_on, 
    updated_by, 
    updated_on, 
    title, 
    id
) 
VALUES (
    'Alice', 
    '2018-05-02 09:56:55.046', 
    NULL(VARCHAR), 
    NULL(TIMESTAMP), 
    'High-Performance Java Persistence, 1st Edition', 
    1
)

INSERT INTO post_tag (post_id, tag_id) VALUES (1, 'JDBC')
INSERT INTO post_tag (post_id, tag_id) VALUES (1, 'Hibernate')
INSERT INTO post_tag (post_id, tag_id) VALUES (1, 'jOOQ')

When updating the Post entity:

Post post = entityManager.find(Post.class, 1L);

post.setTitle(
    "High-Performance Java Persistence, 2nd Edition"
);

The updated_on and update_by columns will be set by the @PreUpdate event listener on the embeddable type:

UPDATE 
    post 
SET 
    created_by = 'Alice', 
    created_on = '2018-05-02 09:56:55.046', 
    updated_by = 'Alice', 
    updated_on = '2018-05-02 09:56:55.106', 
    title = 'High-Performance Java Persistence, 2nd Edition' 
WHERE 
    id = 1

Cool, right?

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Conclusion

So, while previously, you could achieve the same goal using an @EntityListener, now you apply the @PrePersist and @PreUpdate event listeners son the embeddable type, therefore simplifying the implementation.

Subscribe to our Newsletter

* indicates required
10 000 readers have found this blog worth following!

If you subscribe to my newsletter, you'll get:
  • A free sample of my Video Course about running Integration tests at warp-speed using Docker and tmpfs
  • 3 chapters from my book, High-Performance Java Persistence, 
  • a 10% discount coupon for my book. 
Get the most out of your persistence layer!

Advertisements

4 thoughts on “How to use @PrePersist and @PreUpdate on Embeddable with JPA and Hibernate

  1. Hi Vlad, i generate a simple spring boot project, but all field are always stored with null value for me.

  2. what is this LoggedUser class ?
    and how you will get the username in muli user environment ?

    1. It’s explained in my previous article which mentioned in the beginning of this post. That utility is just for testing sake. In a production environment, you could use @RequestScope bean for that.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.