5.1 Czas zmiany danych
Gdy przechowujesz różne rekordy w bazie danych przez wiele lat, często pojawiają się dwa pytania:
- Kiedy ten wpis został dodany do bazy danych?
- Kiedy ten wpis był ostatnio zmieniany?
Są to tak częste zadania, że prawie do każdej tabeli w bazie danych dodawane są dwie kolumny:
- utworzony_czas
- zaktualizowany_czas
Pierwsza przechowuje datę i godzinę utworzenia rekordu, a druga przechowuje datę i godzinę ostatniej modyfikacji. A każda klasa Entity ma pola:
@Entity
@Table(name = "entities")
public class Entity {
...
@Column(name="created_time")
private Date created;
@Column(name="updated_time")
private Date updated;
}
Hibernate może wykonać całą pracę polegającą na kontrolowaniu, kiedy obiekty w bazie danych są aktualizowane za pomocą dwóch adnotacji @CreationTimestamp
i @UpdateTimestamp
.
Przykład:
@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;
}
Kolumny oznaczone tymi adnotacjami zawsze będą przechowywać poprawny czas utworzenia obiektu i jego ostatniej modyfikacji.
5.2 Adnotacja @PrePersist
Jeśli potrzebujesz bardziej złożonych skryptów do kontrolowania czasu obiektu, Hibernate ma również adnotacje dla tego przypadku. Mogą oznaczać metody klas, a Hibernate wywoła te metody, gdy zapisze obiekt w bazie danych. W sumie jest 7 takich adnotacji:
@PrePersist | Wywoływana przed zapisaniem obiektu w bazie danych. (WSTAW SQL) |
@PostPersist | Wywoływana natychmiast po zapisaniu obiektu w bazie danych. (WSTAW SQL) |
@PreRemove | Wywoływana przed usunięciem obiektu z bazy danych. |
@PostUsuń | Wywoływana po usunięciu obiektu z bazy danych. |
@PreUpdate | Wywoływana przed aktualizacją (SQL UPDATE) obiektu w bazie danych. |
@PostAktualizacja | Wywoływana po aktualizacji (SQL UPDATE) obiektu w bazie danych. |
@PostLoad | Wywoływana po załadowaniu obiektu z bazy danych. |
Napiszmy przykład, w którym podajemy klasie właściwy czas tworzenia i aktualizowania jej obiektów:
@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();
}
}
Jeśli Hibernate zapisze obiekt po raz pierwszy, wywoła metodę z adnotacją @PrePersist
. Jeśli zaktualizuje istniejący obiekt w bazie danych, wywoła metodę oznaczoną adnotacją @PreUpdate
.
5.3 Dodawanie naszych EntityListeners
Jeśli naprawdę potrzebujesz, możesz oddzielić metody wywoływane przez Hibernate od obiektu, na którym je wywołuje. Specyfikacja JPA umożliwia zadeklarowanie klas nasłuchiwania, które będą wywoływane w określonych momentach podczas przetwarzania obiektów Entity.
Jeśli masz wiele podobnych obiektów Entity, możesz przenieść niektóre z nich do klasy bazowej i dodać Listener, który kontrolowałby ich zachowanie. Przykład:
@MappedSuperclass
public abstract class BaseEntity {
private Timestamp createdOn;
private Timestamp updatedOn;
}
@Entity
public class User extends BaseEntity {
@Id
private Long id;
private String name;
}
Następnie dla klasy BaseEntity możesz utworzyć specjalną klasę detektora:
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) );
}
}
I możesz połączyć klasę User i jej słuchacza za pomocą kilku adnotacji:
@Entity
@EntityListeners(class= TimeEntityListener.class)
public class User extends BaseEntity {
@Id
private Long id;
private String name;
}