【JPA】@MappedSuperClassでEntityクラスの共通カラムプロパティをまとめる
@MappedSuperClass
主キー用IDや最終更新日時など、RDB上の各テーブルには同一定義のカラムが存在することが少なくありません。
これがJPAのEntityにマッピングされると、複数のEntityクラスに同一定義のカラムプロパティが散在することになります。
(ソースはKotlin)
User.kt
@Entity @Table(name = "USERS") @Where(clause="IS_DELETED=0") data class User( // 頻出 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID", nullable = false) var id: Int? = null, @Column(name = "NAME", nullable = false) val name: String = "", @Column(name = "EMAIL", nullable = false) val email: String = "", // 頻出 @Column(name = "CREATED_AT", nullable = false) val createdAt: Date = Date(), // 頻出 @Column(name = "UPDATED_AT", nullable = false) val updatedAt: Date = Date(), // 頻出 @Column(name = "IS_DELETED", nullable = false) var isDeleted: Boolean = false )
Article.kt
@Entity @Table(name = "ARTICLES") @Where(clause = "IS_DELETED=0") data class Article( // 頻出 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID", nullable = false) var id: Int? = null, @Column(name = "USER_ID", nullable = false) val userId: Int? = null, @Column(name = "TITLE", nullable = false) val title: String = "", @Column(name = "DESCRIPTION", nullable = false) val description: String = "", // 頻出 @Column(name = "CREATED_AT", nullable = false) val createdAt: Date = Date(), // 頻出 @Column(name = "UPDATED_AT", nullable = false) val updatedAt: Date = Date(), // 頻出 @Column(name = "IS_DELETED", nullable = false) var isDeleted: Boolean = false, @ManyToOne(targetEntity = User::class, cascade = [], fetch = FetchType.EAGER, optional = true) @JoinColumn(name = "USER_ID", referencedColumnName = "ID", insertable = false, updatable = false) val user: User? = null )
@MappedSuperClass
で修飾されたクラスを定義し、その中に共通カラムプロパティを定義した上で、各Entityクラスで継承することで、共通のカラムを派生Entity先クラスで書かずに済みます。
( @MappedSuperClass
で修飾されたクラス自体のテーブルへのマッピングは行われません)
TransactionalEntity
/** * 共通するカラムのプロパティをここで定義 */ @MappedSuperclass abstract class TransactionalEntity( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID", nullable = false) var id: Int? = null, @Column(name = "CREATED_AT", nullable = false) val createdAt: Date = Date(), @Column(name = "UPDATED_AT", nullable = false) val updatedAt: Date = Date(), @Column(name = "IS_DELETED", nullable = false) var isDeleted: Boolean = false )
User.kt(修正後)
@Entity @Table(name = "USERS") @Where(clause="IS_DELETED=0") data class User( @Column(name = "NAME", nullable = false) val name: String = "", @Column(name = "EMAIL", nullable = false) val email: String = "", @OneToMany(targetEntity = User::class, cascade = [], fetch = FetchType.LAZY, mappedBy = "id") val articles: List<Article>? = null ) : TransactionalEntity() // TransactionalEntityを継承する
Article.kt(修正後)
@Entity @Table(name = "ARTICLES") @Where(clause = "IS_DELETED=0") data class Article( @Column(name = "USER_ID", nullable = false) val userId: Int? = null, @Column(name = "TITLE", nullable = false) val title: String = "", @Column(name = "DESCRIPTION", nullable = false) val description: String = "", @ManyToOne(targetEntity = User::class, cascade = [], fetch = FetchType.EAGER, optional = true) @JoinColumn(name = "USER_ID", referencedColumnName = "ID", insertable = false, updatable = false) val user: User? = null ) : TransactionalEntity() // TransactionalEntityを継承する
継承の是非等ありますが、かなりスッキリとしたEntityが書けるようになります。