How to update only a subset of entity attributes using JPA and Hibernate

(Last Updated On: January 29, 2018)
Imagine having a tool that can automatically detect if you are using JPA and Hibernate properly. Hypersistence Optimizer is that tool!

Introduction

One of my Twitter followers asked me to answer the following question on StackOverflow.

This article aims to explain various techniques for controlling the UPDATE SQL statement that is executed whenever an entity is modified by the data access layer.

Domain Model

Let’s assume we have the following Post entity:

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

    @Id
    private Long id;

    private String title;

    private long score;

    @Column(
        name = "created_on", 
        nullable = false, 
        updatable = false
    )
    private Timestamp createdOn;

    @Transient
    private String creationTimestamp;

    public Post() {
        this.createdOn = new Timestamp(
            System.currentTimeMillis()
        );
    }

    public String getCreationTimestamp() {
        if(creationTimestamp == null) {
            creationTimestamp = DateTimeFormatter
            .ISO_DATE_TIME.format(
                createdOn.toLocalDateTime()
            );
        }
        return creationTimestamp;
    }

    @Override
    public String toString() {
        return String.format(
            "Post{\n" +
            "  id=%d\n" +
            "  title='%s'\n" +
            "  score=%d\n" +
            "  creationTimestamp='%s'\n" +
            '}', id, title, score, getCreationTimestamp()
        );
    }

    //Getters and setters omitted for brevity
}

Insert-only columns

The Post entity has a createdOn attribute which should only be set when the entity is created for the first time. Any successive UPDATE is not allowed to modify this database column, therefore the updatable attribute of the associated @Column annotation is set to false.

@Column(
    name = "created_on", 
    nullable = false, 
    updatable = false
)
private Timestamp createdOn;

For entity attribute that should never modify after the entity is persisted (insert-only columns), you should use @Column(updatable = false).

Calculated entity attributes

The Post entity createdOn attribute is a Timestamp which we might want to print in application logs using the ISO_DATE_TIME DateTimeFormatter. To avoid calculating the String object representation every time we need to log this entity, the creationTimestamp attribute is going to store this value.

@Transient
private String creationTimestamp;

However, we don’t want this attribute to be persisted. Hence, we need to annotate it with the @Transient. This way, Hibernate is going to ignore this attribute when translating the entity state modifications into an SQL statement.

Assuming we have persisted the following entity:

doInJPA(entityManager -> {
    Post post = new Post();
    post.setId(1L);
    post.setTitle("High-Performance Java Persistence");
    entityManager.persist(post);
});

When fetching, logging, and modifying this entity:

doInJPA(entityManager -> {
    Post post = entityManager.find(Post.class, 1L);
    LOGGER.info("Fetched post: {}", post);
    post.setScore(12);
});

The following output is obtained:

SELECT p.id AS id1_0_0_,
       p.created_on AS created_2_0_0_,
       p.score AS score3_0_0_,
       p.title AS title4_0_0_
FROM   post p
WHERE  p.id = 1

-- Fetched post: Post{
  id=1
  title='High-Performance Java Persistence'
  score=0
  creationTimestamp='2016-10-10T16:48:25.566'
}

UPDATE post
SET    score = 12,
       title = 'High-Performance Java Persistence'
WHERE  id = 1

As you can see, neither the createdOn not the creationTimestamp are included in the UPDATE SQL statement.

For calculated attributes that should never be persisted in the associated database table, you should use @Transient.

Dynamic updates

The previous UPDATE statement includes all table columns, even if only a subset is being actually modified. Using the same SQL statement is beneficial when using JDBC statement caching. However, if the database table is heavily indexed, we don’t want to update certain index entries that have not been modified, as explained by Markus Winand.

For this reason, Hibernate offers the @DynamicUpdate annotation. All we need to do is to add this annotation at the entity level:

@Entity(name = "Post")
@Table(name = "post")
@DynamicUpdate
public class Post {
    //Code omitted for brevity
}

Now, when executing the previous test case which modified the score attribute, the following UPDATE statement is executed:

UPDATE post
SET    score = 12,
WHERE  id = 1

Brilliant!

To update only the columns that got modified, the entity must use the @DynamicUpdate annotation, which is also mandatory when using versionless optimistic locking as well.

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

Conclusion

Flexibility is very important when dealing with a data access framework, and, for this reason, Hibernate offers several options to avoid updating certain columns, or to update just the columns that got modified.

Download free ebook sample

Newsletter logo
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.

4 Comments on “How to update only a subset of entity attributes using JPA and Hibernate

    • All the code is on GitHub and works like a charm. Send me a Pull Request that demonstrates it does not work.

  1. it is not working with the @Dynamicupdate annotation added to my entity java class

    • Send me a Pull Request with a test case in my High-Performance Java Persistence GitHub repository so I can see it too.

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.

Want to run your data access layer at warp speed?