|

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, 2 monthly Q&A calls, monthly coding challenges, a community of like-minded developers, 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.

Similar Posts

38 Comments

  1. Meir Kalter says:

    Hi, it was very helpfull.

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

    1. 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. 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. 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. 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. 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. 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. Thorben Janssen says:

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

  7. Aleph Santos says:

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

    1. 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. Davydov Denys says:

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

    1. 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. 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. 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. Sergio Salmeron says:

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

      1. 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. 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. 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. Betto McRose says:

    finally, a solution for automatic audit info

    GREAT!

    1. 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. Thorben Janssen says:

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

  15. Mark Thomas says:

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

    1. Thorben Janssen says:

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

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

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.