めざましテレビ

今日の早耳ムスメ徳澤直子ちゃーん,お題は「クールにキメたい! 人気のデニム」.
なんと,02/28 (彼女の誕生日だ) 以来の登場!!
待ってたゼ!! 直ちん!!!!
し・か・も
デニム特集ということで着回したっぷり.
それにしてもやっぱりスキニーデニムか... 横浜ではどこで売ってるかよく分からないんだよね.
ともあれ (JW),以外と腰回りにボリュームのある直ちん.
でもでも,脚は細くて決して下半身に重さを感じさせないので,パンツでもミニスカでも見事に決まるんだと思うわけですが...
今日はちょっとボリュームありすぎのような? (^^;
ピチピチっていうよりパンパンって気のせいが...
まぁ,お仕事控えめにしていたくらいだからしょうがないよね.ってことにしておこう.
それにしてもカメラの人,ミニスカートの時に寄りすぎ!!
直ちんの脚がよく見えないじゃないか!!!!
心より無念だよまったくもう.プンプン.


そしてなぜかスタジオへ...
しかも... ナカミーが... デニム穿いちゃってるよ!?
えっと,何かの罰ゲームでしょうか?
っていうか,視聴者への罰ゲーム? (^^;
って思ったけど,そんなに悲惨でもないような.
さすがデニム,万人の味方ですね♪

Kuina-Dao 開発記 Query by Example

予定より遅れてしまいましたが,ようやく Query by Example の基本機能をコミットしました.
Query by Example を使用するには,

public interface EmployeeDao {
    List<Employee> findByExample(Employee employee);
    ・・・
}

のようなメソッドを DAO に定義します.
これで

        Employee emp = new Employee();
        emp.setName("シマゴロー");
        List<Employee> list = employeeDao.findByExample(emp);

のように使うことができます.
引数に渡したエンティティ (永続化されていないインスタンス,「new entity」) の null でないプロパティが検索条件に加えられます.
つまり,上記の場合は

SELECT 
    employee 
FROM 
    Employee AS employee 
WHERE 
    (employee.name = :employee$name)

という JPQL が作成&実行されます.


引数に渡すエンティティのプロパティは ID プロパティ (主キー) も含めて,null でなければ検索条件に含まれてしまいます.
つ・ま・り
プリミティブ型は常に検索条件に含まれてしまいます.
ここは賛否あるかな.
一応,Kuina-Dao 的オススメは,

  • フィールドにプリミティブ型は使用しない.
  • @Id アノテーションはフィールドに指定する.

です.
これにより,余計なプロパティが検索条件に含まれることが回避できます.
この場合,JPA 実装および Kuina-Dao はフィールドアクセスしかしないため,必要なら getter/setter をプリミティブ型にしても構いません.ヌルポ注意.
どのみち Tiger では勝手に変換してくれるのでその必要もあまりないと思いますが.


Query by Example では関連先エンティティのプロパティも検索条件に含めることができます.
例えば ProductCategory の間に多対 1 の関連がある場合,ある Category と関連を持つ Product を全て検索するには次のようにします.
まずは DAO を用意して,

public interface ProductDao {
    List<Product> findByExample(Product product);
    ・・・
}

次のように使います.

        Category category = new Category();
        category.setName("魚");
        Product product = new Product();
        product.setCategory(category);
        List<Product> products = productDao.findByExample(product);

ここでは "魚" という名前を持つ CategoryProduct に設定し,DAO に渡しています.
ここから,

SELECT 
    product 
FROM 
    Product AS product 
        INNER JOIN product.category AS category 
WHERE 
    (category.name = :category$name)

という JPQL が作成&実行されます.


対多関連,つまりコレクションも扱うことができますが,コレクションの要素数が 1 の場合のみ検索条件に含められます.
Kuina-Dao のテストデータは,はぶさんの「SQL 書き方ドリル (isbn:4774122998)」を パクった インスパイヤされたので,EmployeeDepartmentBelongTo を介した多対多になっています.
っていうか,EmployeeBelongTo が 1 対多,BelongToDepartment が多対 1 ですね.
最初の例と同じ DAO の同じメソッドを使って,

        Department dept = new Department();
        dept.setName("営業");
        BelongTo belongTo = new BelongTo();
        belongTo.setDepartment(dept);
        emp = new Employee();
        Set<BelongTo> set = new HashSet<BelongTo>();
        set.add(belongTo);
        emp.setBelongTo(set);
        list = employeeDao.findByExample(emp);

というように,Employee の持つ belongTo プロパティに要素数 1 の Set を設定して,その先の BelongTo に "営業" という名前を持つ Department を設定して DAO に渡しています.
なんか,コードの流れと説明が逆になってるし (苦笑).
ともあれ (JW),ここから

SELECT 
    employee 
FROM 
    Employee AS employee 
        INNER JOIN employee.belongTo AS belongTo 
        INNER JOIN belongTo.department AS department 
WHERE 
    (department.name = :department$name)

という JPQL が作成&実行されます.


現在のところ,関連は内部結合しか使えません.
また,関連も基点になるエンティティから 1 直線に辿ることしかできません.

Employee -> BelongTo -> Department

はできますが,

Employee -> BelongTo
         -> Salary

なんてことはできません.JPQL 的に.


また,現時点では SELECT 句に基点となるエンティティを指定しているため,例えば Product のリストを取得して,その Category にアクセスすると遅延ロードされます.
検索条件に含めているにもかかわらず,後で改めて遅延ロードするのはいかにももったいないわけですが,それを回避するには SELECT 句で複数のエンティティを並べる必要があり,そうすると問い合わせの結果は (基本的に) Object[] になってしまいます.
それはなんだかなーとも思うわけですが,遅延ロードを回避できるようにすべく,

public interface EmployeeDao {
    List<Object[]> findByExample(Employee employee);
    ・・・
}

なんて感じでメソッドの戻り値型が Object[] だったら

SELECT 
    employee,
    belongTo,
    department
FROM 
    Employee AS employee 
        INNER JOIN employee.belongTo AS belongTo 
        INNER JOIN belongTo.department AS department 
WHERE 
    (department.name = :department$name)

という JPQL が作成&実行されるようにしようかな,と.
FETCH JOIN もサポートしたいけど,それはメソッドへのアノテーションで指定かな.


後は,比較条件が '=' だけってのはちょっとアレなので,様々な条件を使えるようにしたいのですが,どうするのが使いやすいか悩ましいです〜.
指定しやすいのはエンティティのフィールドにアノテーションで指定することなんですが...

public class Employee {
    @Id
    private Integer id;

    @Like
    private String name;
    ・・・
}

みたいな.
でもでも,これだと同じプロパティを使って '=' でも比較したいとかいう場合に使えません.
なので,エンティティは諦めて DAO に指定するとするとこんな感じか?

public interface EmployeeDao {
    @Like({"name", "belongTo.department.Name"})
    List<Employee> findByExample(Employee employee);
    ・・・
}

って感じで,アノテーションである演算子を使うプロパティ名を列挙しておくとか.
うーみゅ... ちょっとイマイチかなぁ.
もうちょっと考えるべし.


そうそう,ORDER BY はサポートしたいですね.
これについては

public interface EmployeeDao {
    List<Employee> findByExample(Employee employee, String orderby);
    ・・・
}

って具合に orderby という名前の引数を定義して,

        list = employeeDao.findByExample(emp, "height, weight DESC");

みたいに指定するのはどうかなぁって考えてます.
もっといいアイデアや要望などあれば是非お願いします.