1. Overview

In this article, we’ll learn how to use Hibernate annotations @CreationTimestamp and @UpdateTimestamp to track when we create and update an entity.

2. Models

To illustrate how these annotations operate, we’ll start with a simple Book entity:

@Entity
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    private String title;

    public Book() {
    }

    // standard setters and getters
}

We’ll utilize the H2 database with the schema DDL automatically created based on Book. Hibernate requires that the database column mapped to a field annotated with @CreationTimestamp or @UpdateTimestamp be of a timestamp-based type, such as Timestamp or DateTime.

3. Tracking the Creation Date and Time With @CreationTimestamp

We often need to persist the creation date of an entity. The @CreationtTimestamp is a convenient annotation that sets the field value to the current timestamp when the entity is first saved. Let’s add a field createdOn in Book with this annotation. Since the field needs to store an exact moment in the past, we’ll declare it as Instant, which represents a specific moment in UTC. This field will have the type Timestamp in our generated schema:

@Entity
public class Book {
    //other fields

    @CreationTimestamp
    private Instant createdOn;

    // standard setters and getters

Now, let’s verify that Hibernate sets createdOn after saving a new book:

@Test
void whenCreatingEntity_ThenCreatedOnIsSet() {
    session = sessionFactory.openSession();
    session.beginTransaction();
    Book book = new Book();

    session.save(book);
    session.getTransaction()
      .commit();
    session.close();

    assertNotNull(book.getCreatedOn());
}

4. Tracking the Time of Last Update With @UpdateTimestamp

Similarly, we might demand to note the date/time of the last entity update. The @UpdateTimestamp is another annotation provided by Hibernate. It automatically sets the field value to the current timestamp on each entity update. Let’s add another Instant field called lastUpdatedOn, this time annotated with @UpdateTimestamp:

@Entity
public class Book {
    //other fields

    @UpdateTimestamp
    private Instant lastUpdatedOn;

    // standard setters and getters

Let’s check that Hibernate populates lastUpdatedOn upon entity creation:

@Test
void whenCreatingEntity_ThenCreatedOnAndLastUpdatedOnAreBothSet() {
    session = sessionFactory.openSession();
    session.beginTransaction();
    Book book = new Book();

    session.save(book);
    session.getTransaction()
      .commit();
    session.close();

    assertNotNull(book.getCreatedOn());
    assertNotNull(book.getLastUpdatedOn());
}

We confirmed that Hibernate generates lastUpdatedOn as expected. Let’s also check that lastUpdatedOn changes when we update book, while createdOn stays the same:

@Test
void whenUpdatingEntity_ThenLastUpdatedOnIsUpdatedAndCreatedOnStaysTheSame() {
    session = sessionFactory.openSession();
    session.setHibernateFlushMode(MANUAL);
    session.beginTransaction();

    Book book = new Book();
    session.save(book);
    session.flush();
    Instant createdOnAfterCreation = book.getCreatedOn();
    Instant lastUpdatedOnAfterCreation = book.getLastUpdatedOn();

    String newName = "newName";
    book.setTitle(newName);
    session.save(book);
    session.flush();
    session.getTransaction().commit();
    session.close();
    Instant createdOnAfterUpdate = book.getCreatedOn();
    Instant lastUpdatedOnAfterUpdate = book.getLastUpdatedOn();

    assertEquals(newName, book.getTitle());
    assertNotNull(createdOnAfterUpdate);
    assertNotNull(lastUpdatedOnAfterUpdate);
    assertEquals(createdOnAfterCreation, createdOnAfterUpdate);
    assertNotEquals(lastUpdatedOnAfterCreation, lastUpdatedOnAfterUpdate);
}

After we set a new title for book, only lastUpdatedOn has changed.

5. Source of the Current Date

For the annotations to be useful, we must set the clock correctly to ensure the timestamps are accurate. By default, both of these annotations use the current date of the Java Virtual Machine when setting property values.

Starting from Hibernate 6.0.0, we can optionally specify the database as the source of the date:

@CreationTimestamp(source = SourceType.DB)
private Instant createdOn;
@UpdateTimestamp(source = SourceType.DB)
private Instant lastUpdatedOn;

In this case, the underlying database specifies how to determine the current date. This could, for example, be the database function current_timestamp().

6. Caveats

As we demonstrated before, we set both createdOn and lastUpdatedOn on entity creation.

@Test
void whenCreatingEntity_ThenCreatedOnAndLastUpdatedOnAreEqual() {
    session = sessionFactory.openSession();
    session.beginTransaction();
    Book book = new Book();

    session.save(book);
    session.getTransaction()
      .commit();
    session.close();

    assertEquals(book.getCreatedOn(), book.getLastUpdatedOn());
}

It might be that the differences between creation and update to differ by milliseconds. If, for some reason, we require these two to be equal dates after creation, we should use another method for setting the timestamps. We could achieve this by using JPA @PrePersist and @PreUpdate callbacks as described in Auditing with JPA, Hibernate, and Spring Data JPA.

Furthermore, we have to remember that these annotations only generate new timestamps when data is created and modified by our Java application. They don’t have any effect on a table outside of it. If other applications or SQL scripts modify the book table, we must update our timestamps using different methods.

Instant is part of new APIs for date and time added in Java 8, which are supported out of the box by Hibernate starting from version 5.2.3. It’s recommended to use these new classes for representing dates. They’re available in the java.time package. However, if we need to support older versions of Hibernate or Java, we might have to resort to using different types for timestamps. We can learn more about mapping temporal columns to Java class fields using Hibernate by reading Hibernate – Mapping Date and Time.

7. Conclusion

In this tutorial, we’ve shown how to automatically generate timestamps using @CreationTimestamp and @UpdateTimestamp. Using these annotations is one of the simplest approaches to monitoring the modification dates of an entity. However, when using them we have to keep in mind that Hibernate generates new timestamps on a per-field basis. This leads to multiple timestamps being different, even though they were set by the same INSERT or UPDATE statement.

Additionally, we need to be aware that these annotations don’t create any global mechanisms for a table. If our database table will be modified by different applications, we need to ensure each one of them sets update and creation timestamps correctly.

As always, the complete code samples for this article are available over on GitHub.

Course – LSD (cat=Persistence)

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE
res – Persistence (eBook) (cat=Persistence)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.