home wiki.fukuchiharuki.me
Menu
#author("2023-01-17T10:37:09+00:00","default:haruki","haruki")
* キーワード [#hc7c5f18]
- JPA
- Spring Boot
- Kotlin

* 目次 [#u3ea0b4d]

#contents

* したいこと [#ya88d2e3]

ひとまず最小で基本的な関連と永続化処理を実装したい。

* エンティティ(単体) [#u812d77a]

まずはただIDで識別できる単独の実体。

** モデル [#dda697ef]

 @Entity
 @Table
 class Foo(
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   var id: Long = -1,
 
   var name: String = ""
 )

*** エンティティにはIDが必要 [#c49b023b]

JPAのエンティティはID(プライマリキー)を持つ、のが基本。

** 永続化処理 [#tf12ecc5]

 val entity = Foo(name = "フー")
 entityManager.persist(entity)

*** Kotlinで実装する場合デフォルト値の定義が必要 [#l1e04d92]

Kotlinでエンティティを実装する場合、デフォルト値を定義しておく必要がある。デフォルト値を定義しておかないと、エンティティ永続化時にエラーが発生する。永続化時に値があるかどうかではないことに注意。エラーメッセージからだと原因がなんとも想像つかない。

 detached entity passed to persist spring jpa

* 集約等(1対多)の関係 [#g2c10fb2]

伝票・明細の関係。伝票単位で一式保存するものとして。

** モデル [#x0512dff]

Fooが伝票、Barが明細。

 @Entity
 @Table
 class Foo(
   @OneToMany(mappedBy = "foo", cascade = [CascadeType.ALL], orphanRemoval = true)
   var barList: MutableList<Bar> = emptyList<Bar>().toMutableList()
 
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   var id: Long = -1,
 
   var name: String = "",
 
   @Version
   var version: Long = -1
 )

 @Entity
 @Table
 class Bar(
   @ManyToOne
   @JoinColumn(name = "foo_id", referencedColumnName = "id")
   var foo: Foo? = null,
 
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   var id: Long = -1,
 
   var name: String = ""
 )

*** @OneToManyの設定 [#e05bb408]

: mappedBy | 伝票側エンティティが明細側エンティティから何として関連しているか
: cascade | 明細側エンティティにカスケードする永続化オペレーション
: orphanRemoval | リレーションから削除した明細側エンティティに削除操作を適用する

*** @ManyToOneの設定 [#y0268b0d]

: name | 伝票側エンティティを指定する明細側エンティティ上の外部キー
: referencedColumnName | その外部キーに該当する伝票側エンティティの主キー

*** 伝票側に@Version [#tf9cd8f0]

伝票側エンティティに楽観ロック用のバージョンを設ける。

** 永続化処理 [#y27fae2b]

 val entity = Foo(name = "フー")
   .apply {
     barList.add(
       Bar(foo = this, name = "バー")
     )
   }
 entityManager.persist(entity)

*** 明細側エンティティの永続化処理は不要 [#vb186a99]

永続化オペレーションをカスケードするのでpersistするのは伝票側エンティティだけでいい。

* ログやイベント等(多対1)の関係 [#w9a9673b]

親エンティティに紐づくログやイベント等の関係。親側エンティティの単位で子側エンティティ(ログやイベント等)を一括保存しない。

** モデル [#b1701098]

 @Entity
 @Table
 class Foo(
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   var id: Long = -1
 )

 @Entity
 @Table
 class FooEvent(
   @ManyToOne
   @JoinColumn(name = "foo_id", referencedColumnName = "id")
   var foo: Foo? = null,
 
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   var id: Long = -1
 )

*** @ManyToOneの設定 [#r7579c63]

1対多(集約等)の関係、明細側のエンティティと同じ。

: name | 親側エンティティを指定する子側エンティティ上の外部キー
: referencedColumnName | その外部キーに該当する伝票側エンティティの主キー

** 永続化処理 [#xe866e76]

 entityManager.find(Foo::class.java, fooId)
   ?.let {
     val entity = FooEvent(foo = it)
     entityManager.persist(entity)
     entity // ^let
   }

*** managedな親側エンティティが必要 [#f8f37fe5]

子側エンティティにセットする親側エンティティはmanagedである必要がある(はず)。

* カーソル等(1対1)の関係 [#k9483d82]

TBC

* 任意の関連をもつ(1対0,1)の関係 [#nb058329]

TBC

* 参考 [#sf4400b4]
- [[java - How to use spring Repository without @Id? - Stack Overflow:https://stackoverflow.com/a/29561919]]
- [[エンティティの状態遷移:http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0870/APKC/EU070301.HTM]]
- [[@OneToMany:http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0870/APR4/EU260088.HTM]]
- [[OneToMany (Jakarta EE 仕様 API) - Javadoc:https://spring.pleiades.io/specifications/platform/8/apidocs/javax/persistence/onetomany]]
- [[OneToManyのリストフィールドの削除・更新の仕方 - Qiita:https://qiita.com/yukihigasi/items/14eac33cc2043fcdbddb]]