5.1 Време на промяна на данните
Когато съхранявате различни записи в база данни в продължение на много години, често възникват два въпроса:
- Кога този запис е добавен към базата данни?
- Кога последно е променен този запис?
Това са толкова чести задачи, че две колони се добавят към почти всяка table в базата данни:
- създадено_време
- актуализиран_час
Първият съхранява датата и часа на създаване на записа, а вторият съхранява датата и часа на последната му промяна. И всеки клас 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 ви позволява да декларирате слушателски класове, които ще бъдат извиквани в определени моменти при обработка на обекти 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 можете да създадете специален клас слушател:
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) );
}
}
И можете да свържете потребителския клас и неговия слушател, като използвате няколко анотации:
@Entity
@EntityListeners(class= TimeEntityListener.class)
public class User extends BaseEntity {
@Id
private Long id;
private String name;
}