このページに書いてあること
業務用のシステムを作っていると、DBへのアクセスは非常に一般的な処理で、SpringMVCなんかを使っているとSpring Data Jpaを使ってアクセスするのがよくあるパターンかと思います。
で、DBアクセスをするときによくあるのが、
テーブルがたくさんあるけど、全てのテーブルに共通しているカラムがある。
という状況だと思います。例えば、登録日付・更新日付などが該当しそうです。
このとき、@Entityを使ったコードだとすべてのクラスに同じカラム名のコードが入ってしまうことになります。これは避けるためには共通カラムを親クラスに定義して、差分だけを子クラスが定義してやればよさそうです。
私がこの問題にぶつかったのは、テーブルのカラムは同じだがユーザによって取得先のテーブルを切り替えるという要件ででした。
結論
共通のマッピングだけを引き継ぎ、子ごとに別のテーブルへ保存するなら、親クラスに @MappedSuperclass を付けます。親クラス自体はエンティティとして管理されず、共通列は各子エンティティのテーブルに作られます。
これを実現したい
親クラス ParentUserEntity に共通のID・登録日・更新日を置き、子クラスが継承します。子ごとに @Table で別のテーブルを指定します。

配布例では ChildEntity が ACTIVE_USERS、ArchivedEntity が ARCHIVED_USERS を使います。
親を @Entity にすること自体は可能です。ただし、継承戦略を指定しなければ既定の単一テーブル継承となり、子ごとに別テーブルを持つ今回の要件とは結果が異なります。
子Entityのコードは以下のようになります。
実行用サンプル
単一テーブル継承と、MappedSuperclassで別テーブルへ共通列を引き継ぐ場合を、HibernateとH2で比較します。 ZIPにはソース全文・テスト・pom.xml・READMEを収録しています。JDK 17とMaven 3.9系で mvn test を実行してください。
package info.syachiku.mapping;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "ACTIVE_USERS")
public class ChildEntity extends ParentUserEntity {
protected ChildEntity() {
}
public ChildEntity(String userId, String insertDate, String updateDate) {
super(userId, insertDate, updateDate);
}
}
親も @Entity にした比較用コードです。配布コードの SingleTableChild を保存すると、親で指定した SHARED_USERS に行が入ります。
package info.syachiku.mapping;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "SHARED_USERS")
public abstract class SingleTableParent {
@Id
private String userId;
protected SingleTableParent() {
}
protected SingleTableParent(String userId) {
this.userId = userId;
}
}
親もエンティティにすると、既定の単一テーブル継承では親子が一つのテーブルを共有します。子を別テーブルに分けたいときは、共通の列定義だけを引き継ぐ構成を選びます。
次の修正例では、先に示した ChildEntity の親を、共通列を定義する ParentUserEntity にします。
@MappedSuperclassを使う
次が @MappedSuperclass を付けた親クラスの全文です。この記事の例は javax.persistence を使い、Hibernate 5.6.15.Final と H2 2.3.232 で動作を確認しました。Spring Boot 3以降のアプリでは jakarta.persistence を使う構成に合わせてください。
package info.syachiku.mapping;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
public abstract class ParentUserEntity {
@Id
@Column(name = "USER_ID")
private String userId;
@Column(name = "INSERT_DATE")
private String insertDate;
@Column(name = "UPDATE_DATE")
private String updateDate;
protected ParentUserEntity() {
}
protected ParentUserEntity(String userId, String insertDate, String updateDate) {
this.userId = userId;
this.insertDate = insertDate;
this.updateDate = updateDate;
}
}
テストは ACTIVE_USERS と ARCHIVED_USERS の両方に同じIDの行を保存し、各テーブルから別々の更新日が読めることを確認します。
共通列を持つ親クラスだけのテーブルが作られていないことも確認します。

