本日は11月23日、新嘗祭の日ということで、ありがたくご飯を食べながら記事を書いていこうと思います。
(キンロウカンシャのひ?キンロウってなんですかね…? 🤔 )
今日は、Ruby on Railsでは当たり前のようにDBに生やす、 created_at
と updated_at
をSpring Data JPAでもやっちゃおうという記事です。
Spring Data JPA で作成日時、更新日時を設定する際の2つの問題
Spring Data JPAで作成日時、更新日時をEntityに追加する場合、当たり前ですが、そのままEntityに定義することもできます。
@Entity data class User( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = 0, @field:NotBlank @field:Email var email: String = "", @field:NotBlank var createdAt: Date? = null @field:NotBlank var updatedAt: Date? = null )
そして、各生成、更新の際に以下のように書いたりすることでもできます。
User(
email = "example@example.com",
createdAt = Date(),
updatedAt = Date()
)
とはいえ、各実装ごとにいちいちこうした定義や実装はしたくないですし、それぞれの実装で日時を設定するタイミングが異なってしまったりする可能性もあり、極力共通化したいところです。
@MappedSuperclass でテーブル作成しない親クラスを定義する
基本的に、 作成日時、更新日時を定義する場合、すべてのEntityで定義されていてほしいと思います。
こうした場合、 @MappedSuperclass アノテーションをつけた abstract class
に必要なカラムを定義して、各Entityで継承させることで共通のカラム定義を使い回すことができます。
// 作成・更新日時のカラムを定義した親クラス // @MappedSuperclassをつけているので、テーブル定義はされない @MappedSuperclass abstract class AbstractEntity { @field:NotBlank var createdAt: Date? = null @field:NotBlank var updatedAt: Date? = null } // 継承して、作成・更新日時のカラムが定義されるEntity @Entity data class User( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = 0, @field:NotBlank @field:Email var email: String = "" ) : AbstractEntity
継承になってしまうことで、機能ごとの定義をMixinのような形で使い回すことが難しくなってしまいますが、ほぼ例外なく定義したいカラムがある場合(例えば、今回の作成日時、更新日時や、auto incrementな id など)を共通化するのには非常に便利だと思います。
自動で作成日時、更新日時が設定されるようにする
ここまでで、カラム定義を共通化することができました。
ですが、このままでは作成日時、更新日時の設定は共通化できていません。
ここで使えるのが、各レコードへの処理に対するフックを定義できるアノテーションです。
@PrePersist fun onPrePersist() {} // 新しいEntityが保存される前に呼ばれる @PostPersist fun onPostPersist() {} // 新しいEntityが保存された後に呼ばれる @PreRemove fun onPreRemove () {} // 削除前に呼ばれる @PostRemove fun onPostRemove() {} // 削除後に呼ばれる @PreUpdate fun onPreUpdate() {} // 更新前に呼ばれる @PostUpdate fun onPostUpdate() {} // 更新後に呼ばれる @PreLoad fun onPreLoad() {} // 読み込み前に呼ばれる @PostLoad fun onPostLoad() {} // 読み込み後に呼ばれる
今回は、作成時に日時を設定したいのと、更新時に日時を設定したいので、 @PrePersist
と @PreUpdate
を使います。
@MappedSuperclass abstract class AbstractEntity { // ... 省略 ... @PrePersist fun onPrePersist() { createdAt = Date() updatedAt = Date() } @PreUpdate fun onPreUpdate() { updatedAt = Date() } }
これで、いちいち作成日時や更新日時を設定せずに、自動的に保存することができます。