How do I implement auditing using @CreationTimestamp and @UpdateTimestamp?

In Hibernate (or when using JPA with Hibernate), you can use annotations such as @CreationTimestamp and @UpdateTimestamp to handle automatic auditing for fields such as creation time and last updated time. These annotations are commonly used to automatically populate Date or LocalDateTime fields whenever an entity is created or updated.

Here’s how you would implement auditing using these annotations:

Example of an Entity with Auditing Fields

package org.kodejava.hibernate;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import java.time.LocalDateTime;

@Entity
public class AuditedEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @CreationTimestamp
    @Column(updatable = false) // Ensure this column isn't updated during entity updates
    private LocalDateTime createdAt;

    @UpdateTimestamp
    private LocalDateTime updatedAt;

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public LocalDateTime getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(LocalDateTime createdAt) {
        this.createdAt = createdAt;
    }

    public LocalDateTime getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(LocalDateTime updatedAt) {
        this.updatedAt = updatedAt;
    }
}

Explanation of the Annotations

  1. @CreationTimestamp:
    • This annotation is used to automatically set the creation date/time when a row is first inserted into the database.
    • It gets the timestamp of when the entity is persisted.
  2. @UpdateTimestamp:
    • This annotation is used to automatically update the field with the current timestamp whenever the entity is updated in the database.

Both annotations work with java.util.Date, java.sql.Timestamp, or java.time classes like LocalDateTime.

How It Works

  • The field annotated with @CreationTimestamp is set only once during the insert operation.
  • The field annotated with @UpdateTimestamp is updated every time the entity is updated in the database.

Prerequisites

  • Ensure that Hibernate is being used as the JPA provider.
  • The annotated fields’ values will depend on the database or Hibernate interceptors — meaning Hibernate will manage the timestamps, not your application code.

Use in Application

package org.kodejava.hibernate;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

import java.util.Optional;

public class AuditedEntityService {

    private final EntityManagerFactory entityManagerFactory;

    public AuditedEntityService() {
        this.entityManagerFactory = Persistence.createEntityManagerFactory("my-persistence-unit");
    }

    public AuditedEntity createEntity() {
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();

        AuditedEntity entity = new AuditedEntity();
        entity.setName("Foo");
        em.persist(entity);

        em.getTransaction().commit();
        em.close();

        return entity;
    }

    public Optional<AuditedEntity> findEntityById(Long id) {
        EntityManager em = entityManagerFactory.createEntityManager();
        AuditedEntity entity = em.find(AuditedEntity.class, id);
        em.close();

        return Optional.ofNullable(entity);
    }

    public AuditedEntity updateEntity(Long id) {
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();

        AuditedEntity entity = em.find(AuditedEntity.class, id);
        if (entity == null) {
            em.getTransaction().rollback();
            em.close();
            throw new RuntimeException("Entity not found");
        }

        // Perform update logic (here you can modify any fields if needed)
        entity.setName("Bar");
        em.merge(entity);

        em.getTransaction().commit();
        em.close();

        return entity;
    }

    public void close() {
        entityManagerFactory.close();
    }

    public static void main(String[] args) {
        // Initialize the service
        AuditedEntityService service = new AuditedEntityService();

        // Create operation
        AuditedEntity createdEntity = service.createEntity();
        System.out.println("Created entity with ID: " + createdEntity.getId());

        // Find operation
        Optional<AuditedEntity> foundEntity = service.findEntityById(createdEntity.getId());
        foundEntity.ifPresent(entity -> System.out.println("Found entity, createdAt: " + entity.getCreatedAt()));

        // Update operation
        AuditedEntity updatedEntity = service.updateEntity(createdEntity.getId());
        System.out.println("Updated entity, updatedAt: " + updatedEntity.getUpdatedAt());

        // Close the service
        service.close();
    }
}

Explanation of the Code

  1. EntityManagerFactory:
    • We use an EntityManagerFactory to create instances of EntityManager.
    • EntityManagerFactory is configured based on . persistence.xml
  2. Create Operation:
    • A new EntityManager is instantiated.
    • A transaction is begun using em.getTransaction().begin().
    • The entity is persisted using em.persist().
    • The transaction is committed with em.getTransaction().commit().
  3. Find Operation:
    • The EntityManager is used to find an entity by its ID using the em.find() method.
  4. Update Operation:
    • The entity is first retrieved using the em.find() method.
    • Changes can be made to the entity, and em.merge() is called to persist updates.
    • The updated timestamps are handled automatically by Hibernate.
  5. Manual Dependency Management:
    • Unlike Spring, we don’t have dependency injection here, so the EntityManagerFactory is manually created in the service constructor.
    • main() is used to demonstrate how to use the service.
  6. Closing Resources:
    • In a real application, you’d likely use a try-with-resources or other dedicated resource management techniques to ensure the EntityManager and EntityManagerFactory are properly closed.

Points to Note

  1. @CreationTimestamp and @UpdateTimestamp rely on Hibernate’s automatic timestamp management.
  2. These annotations will not work if you bypass Hibernate and manually interact with the database, as the timestamps are set at the ORM layer.
  3. Use Lombok’s annotations like @Getter, @Setter, or @Data to simplify getter and setter generation.

This approach allows you to automatically manage your audit fields without having to write extra logic for managing timestamps.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.