1. Час зміни даних

Коли ти зберігаєш різні записи у базі даних протягом довгих років, дуже часто виникають два питання:

  • Коли цей запис було додано до бази даних?
  • Коли цей запис змінювався востаннє?

Це настільки часті завдання, що практично до кожної таблиці в базі даних додають дві колонки:

  • created_time
  • updated_time

У першій зберігається дата та час створення запису, а в другій — дата та час її останньої зміни. І в кожному Entity-класі є поля:


@Entity
@Table(name = "entities")
public class Entity {
  ...
 
  @Column(name="created_time")
  private Date created;
 
  @Column(name="updated_time")
  private Date updated;
}

Hibernate може взяти на себе всю роботу з контролю за часом оновлення об'єктів у базі за допомогою двох анотацій — @CreationTimestamp та @UpdateTimestamp.

Приклад:


@Entity
@Table(name = "entities")
public class Entity {
  ...
 
@CreationTimestamp
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "create_date")
    private Date createDate;
 
@UpdateTimestamp
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "modify_date")
private Date modifyDate;
}

У колонках, які позначено цими анотаціями, завжди зберігатиметься правильний час створення об'єкта та його останньої зміни.

2. Анотація @PrePersist

Якщо тобі потрібні якісь складніші сценарії для контролю часу об'єкта, то Hibernate має анотації і на цей випадок. Ними можна позначити методи класу, і Hibernate викличе ці методи, коли зберігатиме об'єкт до бази. Загалом таких анотацій 7:

@PrePersist Викликається перед збереженням об'єкта в базу. (SQL INSERT)
@PostPersist Викликається одразу після збереження об'єкта в базу. (SQL INSERT)
@PreRemove Викликається перед видаленням об'єкта з бази.
@PostRemove Викликається після видалення об'єкта з бази.
@PreUpdate Викликається перед оновленням (SQL UPDATE) об'єкта в базі.
@PostUpdate Викликається після оновлення (SQL UPDATE) об'єкта в базі.
@PostLoad Викликається після того, як об'єкт було завантажено з бази.

Давай напишемо приклад, де ми прописуємо класу правильний час створення та оновлення його об'єктів:


@Entity
@Table(name = "entities")
public class Entity {
  ...
 
  @Column(name="created_time")
  private Date created;
 
  @Column(name="updated_time")
  private Date updated;
 
  @PrePersist
  protected void onCreate() {
    created = New Date();
  }
 
  @PreUpdate
  protected void onUpdate() {
  updated = New Date();
  }
}

Якщо Hibernate зберігає об'єкт вперше, він викличе метод, позначений анотацією @PrePersist. Якщо оновлює існуючий об'єкт у базі, то викличе метод, позначений анотацією @PreUpdate.

3. Додаємо свої EntityListeners

Якщо тобі дуже треба, ти можеш відокремити методи, які викликає Hibernate, від об'єкта, у якого він їх викликає. Специфікація JPA дозволяє тобі оголосити listener-класи, які будуть викликатись у певні моменти обробки Entity-об'єктів.

Якщо у тебе багато схожих Entity-об'єктів, ти можеш винести їхню частину до базового класу і додати Listener, який би керував їхньою поведінкою. Приклад:


@MappedSuperclass
public abstract class BaseEntity {
 
    private Timestamp createdOn;
 
    private Timestamp updatedOn;
 
}


@Entity
public class User extends BaseEntity {
 
     @Id
     private Long id;
 
     private String name;
}

Тоді для класу BaseEntity можна створити спеціальний listener-клас:


public class TimeEntityListener {
 
    public void onPersist(Object entity) {
    if (entity instanceof BaseEntity) {
        BaseEntity baseEntity = (BaseEntity) entity;
        baseEntity.createdOn = now();
    }
    }
 
    public void onUpdate(Object entity) {
    if (entity instanceof BaseEntity) {
        BaseEntity baseEntity = (BaseEntity) entity;
        baseEntity.updatedOn = now();
    }
    }
 
    private Timestamp now() {
    return Timestamp.from(LocalDateTime.now().toInstant(ZoneOffset.UTC) );
    }
}

І зв'язати клас User та його listener можна за допомогою парочки анотацій:


@Entity
@EntityListeners(class=TimeEntityListener.class)
public class User extends BaseEntity {
 
     @Id
     private Long id;
 
     private String name;
}