5.1 データ変更時刻
さまざまなレコードをデータベースに長年保存すると、次の 2 つの疑問が生じることがよくあります。
- このエントリがデータベースに追加されたのはいつですか?
- このエントリが最後に変更されたのはいつですか?
これらは頻繁に行われるタスクであるため、データベース内のほぼすべてのテーブルに 2 つの列が追加されます。
- 作成時間
- updated_time
1 つ目はレコードの作成日時を保存し、2 つ目は最後に変更された日時を保存します。そして、各 Entity クラスにはフィールドがあります。
@Entity
@Table(name = "entities")
public class Entity {
...
@Column(name="created_time")
private Date created;
@Column(name="updated_time")
private Date updated;
}
@CreationTimestamp
Hibernate は、データベース内のオブジェクトが 2 つの注釈とを使用して更新されるタイミングを制御するすべての作業を実行できます@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挿入) |
@PostPersist | オブジェクトがデータベースに保存された直後に呼び出されます。(SQL挿入) |
@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 オブジェクトが多数ある場合は、それらの一部を基本クラスに移動し、その動作を制御するリスナーを追加できます。例:
@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) );
}
}
また、いくつかのアノテーションを使用して User クラスとそのリスナーを接続できます。
@Entity
@EntityListeners(class= TimeEntityListener.class)
public class User extends BaseEntity {
@Id
private Long id;
private String name;
}