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

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 coluns 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 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.

If you liked this article, you might want to subscribe to my newsletter too.

Advertisements

17 thoughts on “How to update only a subset of entity attributes using JPA and Hibernate

  1. Interesting, I didn’t know this was possible. Do you happen to know what was the rationale of defaulting to updating everything? Seems like the wrong default to me…

    1. Statement caching, mostly, was the reason for choosing to update all table columns by default. However, Hibernate has been providing dynamic updates for a long time as well, so you can choose the best option for you specific write-based scenario.

      1. Yes, I imagine that statement caching might be one of the driving reasons (whether explicit, or in hindsight) to justify this behaviour. But have there ever been benchmarks for this? I mean, statement caching can also be enforced by clients by always explicitly setting each attribute, regardless of the change.

      2. That, I cannot tell. Only Gavin knows if he’s done some benchmark in the beginning and he decided that this was the way to go. Some decisions were taken 15 years ago when CPUs were not so powerful as today. For instance, batch ordering is not enabled by default for the very same reason, although, nowadays, the performance penalty of not using batching is much higher on the DB side than the CPU overhead on the client-side. The same goes for caching the SQL statement. Remember how slow J2EE application servers used to work. Back then, you didn’t want to generate the SQL statement on every Session flush because that would slow the server even more.

        For some specific tables, this default behavior might be just fine, while for others using many indexes, it might prove to be very inneficient. It’s up to the application developer to choose what strategy fits best for any given entity.

  2. You should keep in mind some facts:
    Hibernate creates CRUD sql queries on startup, where all fields are included. @DynamicInsert, @DynamicUpdate generate dynamic prepared statements, it means this feature does not work anymore.
    It’s cool when just modified fields will be updated, but from another side many different prepared statements will be created and stored in database query cache.

    1. That’s what I meant when I said that Statement Caching is the reason why this is the default option in Hibernate.

      Only through monitoring and measuring, you can determine if the index penalty is higher than the gain you get by caching the update statements.

  3. Pretty good tip, especially about the issues on using @DynamicUpdate in batch processing. As you well said, just monitoring and measuring we can decide wether @DynamicUpdate is good or bad for out scenario.

  4. Hi Vlad I purchased your book (electronic edition) and waiting for the paper copy to come :).

    I still don’t catch something about OPTIMISTIC LOCKING MODE IN JPA/HIBERNATE

    I load on my browser:

    Person -> House

    meanwhile Entity House is changed on the DB

    Alice thinks of something and based on where the Person loives updates the Person data.
    Alice makes a call to an ejb to persist its change.

    How do we make this call fail using OPTIMISTIC_FORCE_INCREMENT ?
    I ‘,m really struggling with this.

    1. Thanks for buying the book. I’m sure you are going to like it.

      To answer you question, you have two options:

      1. You either acquire a PESSIMISTIC_READ on the associated House entity, therefore preventing any other transaction from modifying this row.
      2. You acquire an OPTIMISTIC_FORCE_INCREMENT lock on on the associated House entity, therefore allowing other transaction to modify this row but rolling back the current transaction if that situation really happens.

      So, for 1. you acquire the lock like this:

      House house = entityManager.find(House.class, person.getHouse().getId(), LockModeType.PESSIMISTIC_READ);

      while for 2, the lock request is:

      House house = entityManager.find(House.class, person.getHouse().getId(), LockModeType.OPTIMISTIC_FORCE_INCREMENT);

  5. I want to use option 2, but I still don’t get it:

    We have to ejb calls involved here, what should I do in the first and in the second ?

    sorry to bother you, Yeah I’m already liking tyour textbook 🙂

    1. My best guess is that:

      In the first call I load and pass to the Alice Person->House Entity wih OPTIMISTIC option (wich should be the default)
      In the second call I reload the same Entity Person->House with OPTIMISTIC_FORCE_INCREMENT
      if the version

      if the version of the entity that Alice passed the second service is equal to the version of the entity i’m reloading I go on
      otherwise I have to raise an exception myself ?

      1. That’s where you get it wrong. You need to read the application-level transaction section from my book (starting from page 112).

        You need a stateful context. SO you have two options:

        1. Either you’re using a Stateful EJB
        2. Or you’re using detached entities. For that, you nerd to preserve on the HHTPP Session the entities that you loaded in the first HTTP request, and in the second request you need to first merge the detached entity, then you need to acquire an OPTIMISTIC_FORCE_INCREMENT on the entity on which you need to protect against a lost update (e.g. `House`).
      1. Thanks, it’s getting more clear 😉 ,I already read that part of your book, I preserve detached entities on the browser and let the client exchange the with the server, I think it’s fine like this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s