10yroの開発日記

福岡にある株式会社10yro(トイロ)のエンジニアが書いています

Spring Data JPA (Kotlin) で複合キーやグループ化されたEntityの定義方法

Spring JPA で複合主キーのテーブルのEntityを作成する方法を記載しています。

また、複合キーの中で一部のキーを用いてグループ化したEntityを作成する方法を紹介します。

※サンプルはKotlinとなっています。

1. 複合キーのEntity作成方法1

以下のような複合キーを持つテーブルのEntityを作成する場合

1-1. ID用のクラスを作成し、@Embeddable アノテーションを付与する。

1-2. 作成したIDクラスの型を持つIDカラムを作成し、@EmbeddedId アノテーションを付与する。

@Table(name = "transactions")
@Entity
open class Transactions : Serializable {
    @Embeddable
    open class Id : Serializable {
        @Column(name = "user_id", nullable = false)
        open var userId: Int? = null

        @Column(name = "product_id", nullable = false)
        open var productId: Int? = null

        @Column(name = "transaction_no", nullable = false)
        open var transactionNo: Int? = null
    }

    @EmbeddedId
    open var id: Id? = null

    @Column(name = "transaction_datetime")
    open var transactionDatetime: Date? = null
}

ID 用のクラスを別に作成しても良いですが、inner class としておけば無駄なクラスを増やさずに済み、

型(Class)指定する際は以下のように全クラス共通して [Class name].Idでアクセスできるため、どのクラスのIDであるというのもわかりやすいです。

(例:Repositoryの定義)

interface TransactionsRepository : JpaRepository<Transactions, Transactions.Id> {
}

IDの値を利用した、別Entityとの関連を持たせたい場合

これにプラスして定義することもできます。

@Table(name = "transactions")
@Entity
open class Transactions : Serializable {
    @Embeddable
    open class Id : Serializable {
        @Column(name = "user_id", nullable = false)
        open var userId: Int? = null

        @Column(name = "product_id", nullable = false)
        open var productId: Int? = null

        @Column(name = "transaction_no", nullable = false)
        open var transactionNo: Int? = null
    }

    @EmbeddedId
    open var id: Id? = null

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false)
    open var user: Users? = null

    @ManyToOne
    @JoinColumn(name = "product_id", nullable = false, insertable = false, updatable = false)
    open var product: Products? = null

    @Column(name = "transaction_datetime")
    open var transactionDatetime: Date? = null
}

2. 複合キーのEntity作成方法2

先ほどの@Embeddableを利用する方法の他に@IdClass アノテーションを利用する方法もあります。

2-1. ID(キー)用のクラスを作成する。

2-2. @IdClass アノテーションを付与し、作成したId用のクラスを指定する。

2-3. 各IDとなるプロパティに @Id アノテーションを付与する。

2-1

open class TransactionsKey : Serializable {
    open var userId: Int? = null

    open var productId: Int? = null

    open var transactionNo: Int? = null
}

2-2, 2-3

@Table(name = "transactions")
@Entity
@IdClass(TransactionsKey::class)
open class Transactions : Serializable {
    @Id
    @Column(name = "user_id", nullable = false)
    open var userId: Int? = null

    @Id
    @Column(name = "product_id", nullable = false)
    open var productId: Int? = null

    @Id
    @Column(name = "transaction_no", nullable = false)
    open var transactionNo: Int? = null

    @Column(name = "transaction_datetime")
    open var transactionDatetime: Date? = null
}

(Repositoryでの指定)

interface MInspectionItemDeformationDisplayRepository : JpaRepository<MInspectionItemDeformationDisplay, TransactionsKey> {
}

3. 複合キーのテーブルで一部キーのみを除いたEntityの作成方法

複合主キーのテーブルにおいて、キーの一部のみでその他はグループ化した形で取得したい場合

(例で言うとユーザが商品ごとに行った取引の最新日時を持つEntityを作成する場合 等)

3-1. @Immutableアノテーションを付与

3-2. @Subselectにて取得したい内容を記載

※ここではHibernateアノテーション (org.hibernate.annotations) @Subselectを使用しています。

@Entity
@Immutable
@Subselect("""
    SELECT user_id, product_id, MAX(transaction_datetime) AS transaction_datetime
    FROM transactions
    GROUP BY user_id, product_id
""")
open class LatestTransactions {
    @Embeddable
    open class Id : Serializable {
        @Column(name = "user_id", nullable = false)
        open var userId: Int? = null

        @Column(name = "product_id", nullable = false)
        open var productId: Int? = null
    }

    @EmbeddedId
    open var id: Id? = null

    @Column(name = "transaction_datetime")
    open var transactionDatetime: Date? = null
}