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
@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.
@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
- EntityManagerFactory:
- We use an
EntityManagerFactory
to create instances ofEntityManager
. EntityManagerFactory
is configured based on .persistence.xml
- We use an
- 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()
.
- A new
- Find Operation:
- The
EntityManager
is used to find an entity by its ID using theem.find()
method.
- The
- 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.
- The entity is first retrieved using the
- 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.
- Unlike Spring, we don’t have dependency injection here, so the
- Closing Resources:
- In a real application, you’d likely use a try-with-resources or other dedicated resource management techniques to ensure the
EntityManager
andEntityManagerFactory
are properly closed.
- In a real application, you’d likely use a try-with-resources or other dedicated resource management techniques to ensure the
Points to Note
@CreationTimestamp
and@UpdateTimestamp
rely on Hibernate’s automatic timestamp management.- These annotations will not work if you bypass Hibernate and manually interact with the database, as the timestamps are set at the ORM layer.
- 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.