【Spring Data JPA】マッピング(Entity)
Spring Data JPA の導入や基本についてはこちらを参照してください。
マッピングの基本
@Id
Entity には必ず主キーとなるフィールドが必要であり、@Id
を付与します。
@Entity
public class User {
@Id
private String id;
private String name;
private int age;
}
@Table, @Column
通常は Entity 名とテーブル名は同じにしますが、異なる Entity 名を使用する場合は@Table
を用いてテーブルと紐付けをします。
またフィールドも同様に、テーブルのフィールド名と異なるフィールドを定義する場合は、@Column
で紐付けをします。
@Entity
@Table(name = 'user')
public class User {
@Id
private String id;
@Column(name = "first_name")
private String name;
private int age;
}
@GeneratedValue
@GeneratedValue
では、主キーを自動で生成する方法として以下のいずれかを指定します。
値 | 説明 |
---|---|
GenerationType.AUTO | DB 毎に適切な生成方法を自動で選択 |
GenerationType.IDENTITY | 自動インクリメント |
GenerationType.SEQUENCE | シーケンスを使用 |
GenerationType.TABLE | キー生成用テーブルを使用 |
自動インクリメントを使用する場合は、以下のように@GeneratedValue
を付与します。
※ Oracle のように自動インクリメントがないものは使用できません。
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
id
は自動インクリメントにより自動で登録されるため、登録項目から外されます。
つまり以下のクエリが実行されることになります。
insert into user (name, age) values (?1, ?2);
@Temporal
日付に関する型のフィールドには、@Temporal
を付与する必要があります。
@Temporal
のプロパティには、フィールドの方に合わせてDate
、Time
、Timestamp
のいずれかを指定します。
@Temporal(TemporalType.DATE)
private Date date;
@Temporal(TemporalType.TIME)
private Date time;
@Temporal(TemporalType.TIMESTAMP)
private Date timestamp;
Date
ではなく、LocalDateTime
を使用する場合は、以下のように@Column
のcolumnDefinition
を設定します。
@Column(columnDefinition = "DATE")
private LocalDate date;
@Column(columnDefinition = "TIME")
private LocalTime time;
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime timestamp;
@Enumerated
以下のような列挙型をフィールドとする場合は、@Enumerated
を付与します。
public enum EnumField {
HOGE,
FUGA,
PIYO;
}
@Enumerated(EnumType.STRING)
private EnumField value;
これにより、'HOGE'
や'FUGA'
といった文字列として DB に設定されます。
参照する際は、列挙型へと変換されます。
また以下のように値を持つ列挙型の場合は、EnumType.ORDINAL
を指定します。
public enum EnumField {
HOGE(0),
FUGA(1),
PIYO(2);
private int status;
EnumField(int status) {
this.status = status;
}
}
@Enumerated(EnumType.ORDINAL)
private EnumField value;
これにより、0
や1
といった値が DB に設定されます。
上記と同じく、参照する際は列挙型に変換されます。
@Embedded / @Embeddable
@Embedded
を付与することで、複数のフィールドを 1 つのクラスにまとめることができます。
まず@Embeddable
を付与したクラスを作成します。
このクラスにまとめたいフィールドを定義します。
@Embeddable
public class UserName {
private String firstName;
private String lastName;
}
あとは Entity で@Embedded
を付与したフィールドを定義するだけです。
@Entity
public class User {
@Id
private String id;
@Embedded
private UserName name;
}
@Embeddable
のフィールド名とテーブルのフィールド名が異なる場合は、Entity 側で以下のように@AttributeOverrides
を設定します。
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "firstName", column = @Column(name = "user_first_name")),
@AttributeOverride(name = "lastName", column = @Column(name = "last_first_name"))
})
private UserName name;
@Embedded
を使用する際はいくつか注意点があります。
1 つ目は JSON の変換です。 デフォルトだと以下のような階層構造になります。
{
"id": "taro",
"name": {
"firstName": "太郎",
"lastName": "山田"
}
}
"name"
を省きたい場合は、@JsonUnwrapped
を使用します。
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "firstName", column = @Column(name = "user_first_name")),
@AttributeOverride(name = "lastName", column = @Column(name = "last_first_name"))
})
@JsonUnwrapped
private UserName name;
{
"id": "taro",
"firstName": "太郎",
"lastName": "山田"
}
2 つ目はクエリの生成です。
例えばfirstName
を条件のレコードを取得する場合は、findByNameFirstName()
と、Entity のフィールド名と@Embeddable
のフィールド名を続けます。
また JPQL でも以下のように Entity と@Embeddable
のフィールドを指定する必要があります。
select u.name.firstName from user u
@Transient
@Transient
を付与した項目は、永続化の対象外となります。
つまり、参照、登録、更新時にこの項目は含まれなくなります。
@Entity
public class User {
@Id
private String id;
private String name;
@Transient
private int age;
}
複合キー
主キーが 2 つ以上あるマッピングの方法です。 正直複雑さが増すので、可能であればサロゲートキー(代替キー)を使用することを勧めます。
@EmbeddedId
主キー用の@Embeddable
を付与したクラスを作成します。
注意点として、必ずSerializable
をimplements
してください。
@Embeddable
public class CartPK implements Serializable {
private static final long serialVersionUID = 4099926204418277863L;
private String userId;
private String productId;
}
Entity クラスでは、@Id
の代わりに@EmbeddedId
を付与します。
@Entity
public class Cart {
@EmbeddedId
private CartPK pk;
private int count;
}
Repository を作成する際は、ジェネリクスに主キー用のクラスを指定してください。
@Repository
public interface CartRepository extends JpaRepository<Cart, CartPK> {
}
findById()
など主キーを指定するメソッドには、主キー用のクラスを指定するようになります。
CartPK pk = new CartPK("taro", "0001");
Cart cart = repository.findById(pk).orElseThrow();
@IdClass
@EmbeddedId
と同様に主キー用のクラスを作成します。@Embeddable
は不要です。
注意点は同じく、Serializable
をimplements
してください。
public class CartPK implements Serializable {
private static final long serialVersionUID = 4099926204418277863L;
private String userId;
private String productId;
}
@EmbeddedId
と異なる点は、Entity でも主キー用のフィールド(@Id
)をそれぞれ定義することです。
まず@IdClass
で主キー用のクラスを指定します。
その後にキーとなるフィールドを主キー用のクラスと同じフィールド名となるように定義します。
@Entity
@IdClass(CartPK.class)
public class Cart {
@Id
private String userId;
@Id
private String productId;
private int count;
}
Repository の作成についてなどは@EmbeddedId
と同じです。
終わりに
この記事ではマッピングの基本について説明していきました。 実際にはこれらに加えて「結合」があります。
時間があれば結合についての記事も書きたいと思っています。