|

How to persist creation and update timestamps with Hibernate


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


Storing the creation timestamp or the timestamp of the last update is a common requirement for modern applications. It sounds like a simple requirement, but for a huge application, you don’t want to set a new update timestamp in every use case that changes the entity.

You need a simple, fail-safe solution that automatically updates the timestamp for each and every change. As so often, there are multiple ways to achieve that:

  • You can use a database update trigger that performs the change on a database level. Most DBAs will suggest this approach because it’s easy to implement on a database level. But Hibernate needs to perform an additional query to retrieve the generated values from the database.
  • You can use an entity lifecycle event to update the timestamp attribute of the entity before Hibernate performs the update.
  • You can use an additional framework, like Hibernate Envers, to write an audit log and get the update timestamp from there.
  • You can use the Hibernate-specific @CreationTimestamp and @UpdateTimestamp annotations and let Hibernate trigger the required updates.

It’s obvious that the last option is the easiest one to implement if you can use Hibernate-specific features. So let’s have a more detailed look at it.

@CreationTimestamp and @UpdateTimestamp

Hibernate’s @CreationTimestamp and @UpdateTimestamp annotations make it easy to track the timestamp of the creation and last update of an entity.

When a new entity gets persisted, Hibernate gets the current timestamp from the VM and sets it as the value of the attribute annotated with @CreationTimestamp. After that, Hibernate will not change the value of this attribute.

The value of the attribute annotated with @UpdateTimestamp gets changed in a similar way with every SQL Update statement. Hibernate gets the current timestamp from the VM and sets it as the update timestamp on the SQL Update statement.

Supported attribute types

You can use the @CreationTimestamp and @UpdateTimestamp with the following attribute types:

  • java.time.LocalDate (since Hibernate 5.2.3)
  • java.time.LocalDateTime (since Hibernate 5.2.3)
  • java.util.Date
  • java.util.Calendar
  • java.sql.Date
  • java.sql.Time
  • java.sql.Timestamp

Example

Let’s have a look at an example entity that uses the 2 annotations to store the timestamp of its creation and last update.

As you can see in the following code snippet, I just added the @CreationTimestamp annotation to the createDateTime attribute and the @UpdateTimestamp annotation to the updateDateTime attribute.

@Entity
public class MyEntity {

	@Id
	@GeneratedValue
	private Long id;

	private String value;

	@CreationTimestamp
	private LocalDateTime createDateTime;

	@UpdateTimestamp
	private LocalDateTime updateDateTime;

	…

}

When you persist a new MyEntity, Hibernate will get the current time from the VM and store it as the creation and update timestamp. As you can see in the log output, Hibernate gets a new timestamp for each attribute. The creation and update timestamp will therefore not be the same even if the entity was never updated.

MyEntity e = new MyEntity();
em.persist(e);
15:35:49,785 DEBUG SQL:92 – insert into MyEntity (createDateTime, updateDateTime, value, id) values (?, ?, ?, ?)
15:35:49,789 TRACE BasicBinder:65 – binding parameter [1] as [TIMESTAMP] – [2016-10-10T15:35:49.772]
15:35:49,791 TRACE BasicBinder:65 – binding parameter [2] as [TIMESTAMP] – [2016-10-10T15:35:49.776]
15:35:49,792 TRACE BasicBinder:53 – binding parameter [3] as [VARCHAR] – [null]
15:35:49,793 TRACE BasicBinder:65 – binding parameter [4] as [BIGINT] – [1]

Hibernate will change the update timestamp with each SQL Update statement and keep the creation timestamp unchanged. But you might be surprised, when you see the generated SQL Update statement. It also updates the creation timestamp and sets it to its initial value.

e = em.find(MyEntity.class, 1L);
e.setValue(“A Value”);
15:35:49,804 DEBUG SQL:92 – update MyEntity set createDateTime=?, updateDateTime=?, value=? where id=?
15:35:49,804 TRACE BasicBinder:65 – binding parameter [1] as [TIMESTAMP] – [2016-10-10T15:35:49.772]
15:35:49,805 TRACE BasicBinder:65 – binding parameter [2] as [TIMESTAMP] – [2016-10-10T15:35:49.804]
15:35:49,805 TRACE BasicBinder:65 – binding parameter [3] as [VARCHAR] – [A Value]
15:35:49,805 TRACE BasicBinder:65 – binding parameter [4] as [BIGINT] – [1]

Summary

Tracking the creation and last update timestamp of a database record is a common requirement. As you’ve seen, Hibernate’s @CreationTimestamp and @UpdateTimestamp annotations make it easy to implement. You just have to add an annotation to an entity attribute and Hibernate will take care of the necessary updates.

38 Comments

  1. Avatar photo Meir Kalter says:

    Hi, it was very helpfull.

    some minutes of work exectly in the example – and it was ready.

    1. Avatar photo Thorben Janssen says:

      Awesome 🙂

  2. Its doing good, but when i update the entity hibernate making created date time null.
    Please help on this.

    1. Avatar photo Thorben Janssen says:

      Hi Abdul,

      can you please share the code.
      The mapping works fine for me.

      Regards,
      Thorben

  3. need to use @Column(updatable = false) on insertTimeStamp column. otherwise JPA updates it as null.
    I am Using SpringBoot2 with JPA

    1. Avatar photo Thorben Janssen says:

      I can’t reproduce the problem. Hibernate doesn’t change the insertTimeStamp when I update the entity.
      Which SpringBoot and Hibernate version do you use?

    2. I had the same problem as Abdul, @Column(updatable = false) solved the problem.
      Thanks Niraj

  4. Avatar photo RICARDO PALAZZIO says:

    I had conflict when use @CreationTimestamp and @UpdateTimestamp with @Version annotation.
    I got a optimistic locking. I think this not looks at the version column on update. Do you have too ?
    … when a have some time free i’ll try fix it.

    Grats Thorben!

    1. Avatar photo Thorben Janssen says:

      I tested it with multiple Hibernate 5.x releases and it worked fine with all of them.
      Which Hibernate version do you use?

  5. During first time insertion, update timestamp will be null right?. As it is applicable only during update.
    Is there a feature like first time it should be populated with create timestamp, and on every update it should get updated. Can you please brief about the behaviour.

    1. Avatar photo Thorben Janssen says:

      The update timestamp is never null. It gets set in the SQL INSERT statement but it doesn’t contain the same value as the insert timestamp.

  6. When I merge an existing record it updates the updatedon field but it sets the created on field to null.
    Why?

    1. Avatar photo Thorben Janssen says:

      I can’t reproduce that problem with any Hibernate 5 version. Which Hibernate version do you use?

  7. Avatar photo Aleph Santos says:

    Is there a way to set a specific timezone like “America/Sao_Paulo”?

    1. Avatar photo Thorben Janssen says:

      Hibernate uses the timezone configured for your JVM. So, if you want to use a specific one, just make sure that it’s used by your JVM.

  8. Avatar photo Davydov Denys says:

    thank you very much you save my time, but I’m interested can i applying two annotations to one field

    1. Avatar photo Thorben Janssen says:

      That depends… Which annotations do you want to apply?

      You can’t apply the @CreationTimestamp and @UpdateTimestamp annotations to the same field. In that case, you should only use the @UpdateTimestamp annotation because it also gets set when you persist the entity.

  9. Thorben, great article!

    very simple solution to persist update time from VM but if you want to persist time from DB what is the best approach?
    I know that @PrePersist and @PreUpdate can be used for this but I want to avoid additional query.
    On DB level we can do something like this: update xxxTable set time_modified = current_timestamp, column1 = ?, … where id = ?. Is there anything in Hibernate to achieve this?

    Thanks
    Milan

    1. Avatar photo Thorben Janssen says:

      Thanks Milan.

      You could use a database trigger to set the time_modified field on each insert and update statement. If you annotate the corresponding entity attribute with @Generated, Hibernate makes sure to get the generated value from the database and to update the value of the entity attribute. But that, of course, requires an additional query. You can learn more about it at Hibernate Tips: Map generated values

      Regards,
      Thorben

  10. Avatar photo Sergio Salmeron says:

    Im trying to use the annotations with Hibernate 5.2.10 Final but im still getting the error: Unsupported Property type for generator annotation @CreationTimestamp
    I’m using it with LocalDateTime type.

    1. Avatar photo Sergio Salmeron says:

      Im using JPA 2.1 annotations.
      Should I use Hibernate specific configuration?

      1. Avatar photo Thorben Janssen says:

        These are Hibernate-specific annotations that are not supported by any JPA version.
        You need to use Hibernate as your JPA implementation at compile and runtime. You also need to make sure that your Hibernate version supports the Date and Time API as an attribute type (see Hibernate 5: How to persist LocalDateTime & Co with Hibernate)

    2. Avatar photo Thorben Janssen says:

      Are you sure that you’re using Hibernate 5.2.10.Final at runtime?
      This looks like an error message that you would get with an older version.
      I tested it with Hibernate 5.2.10 and it works fine.

  11. How does it work in integration tests ? It would be nice that hibernate could be parameterized with a Clock object

    1. Avatar photo Thorben Janssen says:

      Unfortunately, you can’t parameterize it. Hibernate uses the JVM to get the current time. So, you would need to control that …

  12. Wow!! I am quite eager to use this in my project. Thanks!!

  13. Avatar photo Betto McRose says:

    finally, a solution for automatic audit info

    GREAT!

    1. Avatar photo Thorben Janssen says:

      Hi Betto,

      it only persists the timestamps so it’s not a real audit solution. But if you don’t need to persist any additional information (who changed what), this is the easiest solution I know.

  14. Very interesting!

    I didn’t know those annotations. Usually I solve this kind of feature with Entity Listeners or method callbacks.

    Great article!

    1. Avatar photo Thorben Janssen says:

      Thanks Rafael!
      Method callbacks are also a good approach but these annotations are easier to use.

  15. Avatar photo Mark Thomas says:

    You can also use JPA @PrePersist and @PreUpdate annotations to set creation and update timestamps, respectively.

    1. Avatar photo Thorben Janssen says:

      Sure, but why implement it yourself if there is already a feature for it?

      1. Avatar photo Mohammed Salman says:

        Hi Thorben,
        Great article. Was looking for something like this! Actually I was looking for something which would use database timestamp rather than Java VM timestamps because it happens sometimes that db time and application server time are out of sync. But even this trick is good enough. But I’m using Spring Data JPA with Hibernate Implementation. I have avoided using hibernate annotations and kept it purely jpa in my classes. Just want to ask if it is alright to use hibernate annotation or use some jpa alternative? And do you have any suggestions if want to save using DB time rather than Application Time. One simplest and worst way I can think of is calling the db to get it’s current time use it to set in the java code but I guess it’s not optimal solution.

        1. Avatar photo Thorben Janssen says:

          Hi Mohammed,

          Do you plan to replace Hibernate with a different JPA implementation?
          In my experience, that almost never happens. If you don’t expect to switch to a different JPA implementation, you can use Hibernate annotations in your mapping.

          If you don’t want to use any Hibernate-specific annotations, you could use an EntityListener or lifecycle callback instead.

          Regards,
          Thorben

Leave a Reply to Rafael Ponte Cancel 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.