JPA関連アノテーションの基本として-その2-


@OneToOneアノテーション

UserとUserAttributeが1対1の関係を作成します。

@Entity
public class User {
    private Long id;
    private String name;
    private UserAttribute attribute;

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    @OneToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
    public UserAttribute getAttribute() { return attribute; }
    public void setAttribute(UserAttribute attribute) { this.attribute = attribute; }
}
@Entity @Table(name="USER_ATTR")
public class UserAttribute {
    private Long id;

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
}

とすることで以下のようなテーブルが作成されます。
USER

ID NAME ATTRIBUTE_ID
1 Thome 1

USER_ATTR

ID
1


テーブルへの永続化は、以下の様にUserの保存のみで2つのテーブルへ行われます。

    User u = new User();
    UserAttribute ua = new UserAttribute();
    u.setName("Thome");
    u.setAttribute(ua);
    getHibernateTemplate.save(u);


上記では、「fetch=FetchType.LAZY」としているため、Userを取得時には以下の様なSQLが発行されます。

select
    user0_.id as id1_0_,
    user0_.attribute_id as attribute3_1_0_,
    user0_.name as name1_0_ 
from
    User user0_ 
where
    user0_.id=1L

UserクラスのgetAttribute()を呼び出した時点で、USER_ATTRテーブルへのSQLが発行されます。


「fetch=FetchType.EAGER」とすると以下のようなSQLが発行され、2つのクラスが同時に得られます。

select
    user0_.id as id1_1_,
    user0_.attribute_id as attribute3_1_1_,
    user0_.name as name1_1_,
    userattrib1_.id as id2_0_ 
from
    User user0_ 
left outer join
    USER_ATTR userattrib1_ 
        on user0_.attribute_id=userattrib1_.id 
where
    user0_.id=1L

@OneToManyと@ManyToOneアノテーション

Order(注文)とOrderLine(注文明細)の1対多関係のリレーションはをアノテーションで指定します。それぞれのクラス間はお互いに参照を持つ双方向関連のクラス定義としています。注文テーブルは以下の様に@OneToManyを指定する。mappedByには双方向関連の参照される側のプロパティ名を指定します。

@Entity
@Table(name="ORDER_TBL")
public class Order implements Serializable {
    private Long id;
    private Set<OrderLine> orderDetails = new HashSet<OrderLine>();
    
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() { return id; }

    @OneToMany(mappedBy="order")
    public Set<OrderLine> getOrderLines() { return orderLines; }
    // ・・・
}

注文明細側は、@ManyToOne を指定。

@javax.persistence.Entity
public class OrderLine implements Serializable {
    private Long id;
    private Order order;

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() { return id; }

    @ManyToOne
    public Order getOrder() { return order; }
    // ・・・
}

これにより1対多関連がマッピングされます。


@OneToManyにfetchとして以下のように指定するとイガーフェッチとなります。Orderをデータベースから取得した場合、関連するOrderLineを同時に取得します。

@Entity
@Table(name="ORDER_TBL")
public class Order implements Serializable {
    // ・・・
    @OneToMany(mappedBy="orderContent", fetch=FetchType.EAGER)
    public Set<OrderLine> getOrderLines() { return orderLines; }

Callback系アノテーション

Entityの永続化操作のタイミングで任意の操作を行うためにコールバック用のアノテーションが用意されています。

@PrePersist オブジェクトの永続化前に実行する処理を指定
@PostPersist オブジェクトの永続化後に実行する処理を指定
@PreUpdate オブジェクトの属性が更新前に実行する処理を指定
@PostUpdate オブジェクトの属性が更新後に実行する処理を指定
@PreRemove オブジェクトの削除前に実行する処理を指定
@PostRemove オブジェクトの削除後に実行する処理を指定
@PostLoad オブジェクトのデータがデータベースから読み込まれる前に実行する処理を指定

Entityクラスのメソッドに上記アノテーションを指定することで、任意の操作が実現できます。典型的な例では、更新時に更新日時と更新者IDを埋め込むような場合に重宝するでしょう。

  @PreUpdate
  private void preUpdate() {
    setUpdateDate(new Date());
    setUpdateUser(getUserId());
  }

Customerの年齢を永続化のタイミングで更新したい場合など、

@Entity
public class Customer {
  // ・・・
  @PostLoad
  @PostPersist
  @PostUpdate
  public void calculateAge() {
    // 年齢計算して設定
  }

@Where

Hibernate独自のアノテーション。クラスに付与することで、エンティティへのアクセス時の条件を指定できます。

@Entity
@Where(clause="delflg <> 1")
public class Customer {

クラスに付けると、そのEntityのアクセスのwhere句としして、"delflg <> 1"の条件を付与してくれます。





Pro JPA 2 (Expert's Voice in Java)

Pro JPA 2 (Expert's Voice in Java)