NetBeans6.8 BetaでJPA2.0を試してみる。 - その6

今回はJoinです。

Joinの中でのJPQL独自と言っていいパス式によるJoinを試してみたいと思います。

下記のようなEntityがあるとします(CustomerとDiscountCodeの関係は1対多 多対1。今回はMetamodelは省略)

@Entity
@Table(name = "CUSTOMER")
public class Customer implements Serializable {

    @Id
    @Column(name = "CUSTOMER_ID")
    private Integer customerId;

    @Column(name = "NAME")
    private String name;

    @JoinColumn(name = "DISCOUNT_CODE", referencedColumnName = "DISCOUNT_CODE")
    @ManyToOne(optional = false)
    private DiscountCode discountCode;

//アクセサは省略
@Entity
@Table(name = "DISCOUNT_CODE")
public class DiscountCode implements Serializable {

    @Id
    @Column(name = "DISCOUNT_CODE")
    private String discountCode;

    @Column(name = "RATE")
    private BigDecimal rate;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "discountCode")
    private List<Customer> customerList;
//アクセサ省略

ここで、DiscountCodeのrate(DISCOUNT_CODEテーブルのRATEカラム)の値が10以上であるCustomerを取得したい場合にJPQLでは下記のようにパス式と呼ばれる記述が可能です。

SELECT c FROM Customer c WHERE c.discountCode.rate > 10

追記 11/13
すみません、上記は間違ってました。正しくは以下の文ですね(「>」か「>=」の違い)

SELECT c FROM Customer c WHERE c.discountCode.rate >= 10


「.」で繋げることで最終的に条件を指定したいエンティティの属性までたどれます(ただし「.」で繋げられるのは関連元のエンティティから見た場合に非コレクション型のフィールド)
自分はこの書き方が好きなのでこの書き方が可能な場合は常にこれで書いてます。

んで、上記の場合に発行されるSQLは下記になります。(SELECTは*で省略してます)

SELECT
    *
FROM
    DISCOUNT_CODE t0, CUSTOMER t1 
WHERE 
   ((t0.RATE >= 10) AND (t0.DISCOUNT_CODE = t1.DISCOUNT_CODE))

ではこのSQLをJPA2.0のCriteriaで書くとどうなるかというと下記のような感じです。

CriteriaQuery<Customer> cq = qb.createQuery(Customer.class);
Root<Customer> r = cq.from(Customer.class);
cq.select(r).
    where(qb.ge(r.get(Customer_.discountCode).
                     get(DiscountCode_.rate),new BigDecimal("10")));

whereのすぐ後にくる「qb.ge」というのは「qb.greaterThanOrEqualTo」メソッドのシンボリックリンクです。
以降の文を文章にするとCustomerからdiscountCode取って、そこからさらにDiscountCodeのrateを取るって感じでしょうか。
JPQLのパス式と同じように「Joinするぞ!」っていうのをあんまり意識すること無く書けます。その書き方が良いと思えるかどうかは人それぞれでしょうが(´д`)

とりあえず今回はこのへんで。次回は外部結合やろうかなと思いますです。