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;
}