5.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;
}
В колонках, помеченных данными аннотациями, всегда будет храниться правильное время создания объекта и его последнего изменения.
5.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
.
5.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;
}
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ