@Entityの詳細設定
@Entityの詳細設定
Section titled “@Entityの詳細設定”JPA(Java Persistence API)における@Entityアノテーションは、Javaクラスをデータベースのテーブルに対応付けるための重要なアノテーションです。この章では、@Entityとその関連アノテーションの詳細な設定方法について解説します。
@Entityアノテーションの基本
Section titled “@Entityアノテーションの基本”@Entityアノテーションは、クラスがJPAエンティティであることを示します。
@Entity@Table(name = "users")public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
private String name; private String email;}@Tableアノテーションの詳細設定
Section titled “@Tableアノテーションの詳細設定”@Tableアノテーションは、エンティティがマッピングされるテーブルの詳細を指定します。
@Entity@Table( name = "users", // テーブル名 schema = "public", // スキーマ名 catalog = "mydb", // カタログ名 uniqueConstraints = { // ユニーク制約 @UniqueConstraint(name = "uk_email", columnNames = {"email"}) }, indexes = { // インデックス @Index(name = "idx_name", columnList = "name"), @Index(name = "idx_email", columnList = "email") })public class User { // ...}複合インデックスの設定
Section titled “複合インデックスの設定”@Entity@Table( name = "orders", indexes = { @Index(name = "idx_user_date", columnList = "user_id,order_date"), @Index(name = "idx_status_date", columnList = "status,order_date DESC") })public class Order { // ...}@Columnアノテーションの詳細設定
Section titled “@Columnアノテーションの詳細設定”@Columnアノテーションは、フィールドがマッピングされるカラムの詳細を指定します。
@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@Column( name = "user_name", // カラム名 nullable = false, // NULL許可 unique = true, // ユニーク制約 length = 100, // 文字列の長さ precision = 10, // 数値の精度(小数点を含む桁数) scale = 2, // 数値のスケール(小数点以下の桁数) insertable = true, // INSERT可能か updatable = true, // UPDATE可能か columnDefinition = "VARCHAR(100) NOT NULL" // カラム定義(DDL生成時) ) private String name;
@Column( name = "email_address", nullable = false, unique = true, length = 255 ) private String email;
@Column( name = "balance", precision = 19, scale = 2, nullable = false ) private BigDecimal balance;
@Column( name = "created_at", nullable = false, updatable = false, // 作成後は更新不可 columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP" ) private LocalDateTime createdAt;}主キーの設定
Section titled “主キーの設定”@Idアノテーション
Section titled “@Idアノテーション”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;}@GeneratedValueの戦略
Section titled “@GeneratedValueの戦略”@Entitypublic class User { // IDENTITY: データベースの自動採番(MySQL、PostgreSQL、SQL Server) @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
// SEQUENCE: シーケンスを使用(Oracle、PostgreSQL) @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq") @SequenceGenerator(name = "user_seq", sequenceName = "user_sequence", allocationSize = 1) private Long id;
// TABLE: テーブルを使用したID生成 @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "user_gen") @TableGenerator( name = "user_gen", table = "id_generator", pkColumnName = "gen_name", valueColumnName = "gen_value", pkColumnValue = "user_id", allocationSize = 1 ) private Long id;
// AUTO: データベースに応じて自動選択 @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;}@Entity@IdClass(OrderItemId.class)public class OrderItem { @Id private Long orderId;
@Id private Long productId;
private int quantity;}
// 複合主キー用のクラスpublic class OrderItemId implements Serializable { private Long orderId; private Long productId;
// equals, hashCodeの実装が必要}
// または@EmbeddedIdを使用@Entitypublic class OrderItem { @EmbeddedId private OrderItemId id;
private int quantity;}
@Embeddablepublic class OrderItemId implements Serializable { @Column(name = "order_id") private Long orderId;
@Column(name = "product_id") private Long productId;
// equals, hashCodeの実装が必要}@OneToOne
Section titled “@OneToOne”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@OneToOne( cascade = CascadeType.ALL, // カスケード操作 fetch = FetchType.LAZY, // フェッチ戦略 orphanRemoval = true // 孤立エンティティの削除 ) @JoinColumn( name = "profile_id", // 外部キーカラム名 referencedColumnName = "id", // 参照先のカラム名 nullable = false, // NULL許可 unique = true // ユニーク制約 ) private UserProfile profile;}
@Entitypublic class UserProfile { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@OneToOne(mappedBy = "profile") private User user;}@OneToMany
Section titled “@OneToMany”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@OneToMany( mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true ) @OrderBy("createdAt DESC") // ソート順 private List<Order> orders = new ArrayList<>();}
@Entitypublic class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user;}@ManyToMany
Section titled “@ManyToMany”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@ManyToMany( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY ) @JoinTable( name = "user_roles", // 中間テーブル名 joinColumns = @JoinColumn(name = "user_id"), // 自分側の外部キー inverseJoinColumns = @JoinColumn(name = "role_id") // 相手側の外部キー ) private Set<Role> roles = new HashSet<>();}
@Entitypublic class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@ManyToMany(mappedBy = "roles") private Set<User> users = new HashSet<>();}カスケード操作の詳細
Section titled “カスケード操作の詳細”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
// ALL: すべての操作をカスケード @OneToOne(cascade = CascadeType.ALL) private UserProfile profile;
// PERSIST: 保存時のみカスケード @OneToMany(cascade = CascadeType.PERSIST) private List<Order> orders;
// MERGE: マージ時のみカスケード @OneToMany(cascade = CascadeType.MERGE) private List<Order> orders;
// REMOVE: 削除時のみカスケード @OneToMany(cascade = CascadeType.REMOVE) private List<Order> orders;
// REFRESH: リフレッシュ時のみカスケード @OneToMany(cascade = CascadeType.REFRESH) private List<Order> orders;
// DETACH: デタッチ時のみカスケード @OneToMany(cascade = CascadeType.DETACH) private List<Order> orders;
// 複数のカスケードタイプを指定 @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) private List<Order> orders;}フェッチ戦略
Section titled “フェッチ戦略”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
// EAGER: 即座にロード(推奨されない場合が多い) @OneToOne(fetch = FetchType.EAGER) private UserProfile profile;
// LAZY: 遅延ロード(デフォルト、推奨) @OneToMany(fetch = FetchType.LAZY) private List<Order> orders;
// @EntityGraphを使用した動的なフェッチ @OneToMany(fetch = FetchType.LAZY) @EntityGraph(attributePaths = {"orderItems"}) private List<Order> orders;}バージョン管理(楽観的ロック)
Section titled “バージョン管理(楽観的ロック)”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@Version private Long version; // 楽観的ロック用のバージョン番号
private String name;}エンベッダブルクラス
Section titled “エンベッダブルクラス”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@Embedded @AttributeOverrides({ @AttributeOverride(name = "street", column = @Column(name = "home_street")), @AttributeOverride(name = "city", column = @Column(name = "home_city")), @AttributeOverride(name = "zipCode", column = @Column(name = "home_zip_code")) }) private Address homeAddress;
@Embedded @AttributeOverrides({ @AttributeOverride(name = "street", column = @Column(name = "work_street")), @AttributeOverride(name = "city", column = @Column(name = "work_city")), @AttributeOverride(name = "zipCode", column = @Column(name = "work_zip_code")) }) private Address workAddress;}
@Embeddablepublic class Address { private String street; private String city;
@Column(name = "zip_code") private String zipCode;
// getter/setter}列挙型のマッピング
Section titled “列挙型のマッピング”@Entitypublic class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
// ORDINAL: 序数で保存(0, 1, 2...) @Enumerated(EnumType.ORDINAL) private OrderStatus status;
// STRING: 文字列で保存(推奨) @Enumerated(EnumType.STRING) @Column(length = 20) private OrderStatus status;
// カスタムコンバーターを使用 @Convert(converter = OrderStatusConverter.class) private OrderStatus status;}
public enum OrderStatus { PENDING, PROCESSING, COMPLETED, CANCELLED}
@Converter(autoApply = true)public class OrderStatusConverter implements AttributeConverter<OrderStatus, String> { @Override public String convertToDatabaseColumn(OrderStatus status) { if (status == null) { return null; } return status.name().toLowerCase(); }
@Override public OrderStatus convertToEntityAttribute(String dbData) { if (dbData == null) { return null; } return OrderStatus.valueOf(dbData.toUpperCase()); }}日時型のマッピング
Section titled “日時型のマッピング”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
// LocalDate: DATE型にマッピング @Column(name = "birth_date") private LocalDate birthDate;
// LocalTime: TIME型にマッピング @Column(name = "login_time") private LocalTime loginTime;
// LocalDateTime: TIMESTAMP型にマッピング @Column(name = "created_at", nullable = false, updatable = false) private LocalDateTime createdAt;
// ZonedDateTime: TIMESTAMP WITH TIME ZONE型にマッピング @Column(name = "updated_at") private ZonedDateTime updatedAt;
// カスタムコンバーターを使用 @Convert(converter = LocalDateTimeConverter.class) private LocalDateTime customDateTime;}コレクションのマッピング
Section titled “コレクションのマッピング”@Entitypublic class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
// @ElementCollection: 基本型やエンベッダブルのコレクション @ElementCollection @CollectionTable(name = "user_tags", joinColumns = @JoinColumn(name = "user_id")) @Column(name = "tag") private List<String> tags = new ArrayList<>();
// エンベッダブルのコレクション @ElementCollection @CollectionTable(name = "user_phone_numbers", joinColumns = @JoinColumn(name = "user_id")) private List<PhoneNumber> phoneNumbers = new ArrayList<>();
// Mapのマッピング @ElementCollection @CollectionTable(name = "user_preferences", joinColumns = @JoinColumn(name = "user_id")) @MapKeyColumn(name = "pref_key") @Column(name = "pref_value") private Map<String, String> preferences = new HashMap<>();}
@Embeddablepublic class PhoneNumber { private String type; // HOME, WORK, MOBILE private String number;
// getter/setter}// 単一テーブル継承(SINGLE_TABLE)@Entity@Inheritance(strategy = InheritanceType.SINGLE_TABLE)@DiscriminatorColumn(name = "user_type", discriminatorType = DiscriminatorType.STRING)public abstract class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;}
@Entity@DiscriminatorValue("ADMIN")public class AdminUser extends User { private String adminLevel;}
@Entity@DiscriminatorValue("CUSTOMER")public class CustomerUser extends User { private String customerLevel;}
// テーブルごとのクラス継承(TABLE_PER_CLASS)@Entity@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)public abstract class User { @Id @GeneratedValue(strategy = GenerationType.TABLE) private Long id;}
@Entitypublic class AdminUser extends User { private String adminLevel;}
// 結合テーブル継承(JOINED)@Entity@Inheritance(strategy = InheritanceType.JOINED)public abstract class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;}
@Entity@PrimaryKeyJoinColumn(name = "user_id")public class AdminUser extends User { private String adminLevel;}これらの設定を適切に組み合わせることで、データベーススキーマとJavaオブジェクトのマッピングを効率的に管理できます。プロジェクトの要件に応じて、最適な設定を選択することが重要です。