// 下階層用テンプレート #topicpath ---- //ここにコンテンツを記述します。 #contents データは以下のようになっているとします。 mysql> select * from Samples.CUSTOMER; +----+-----------+---------+ | ID | NAME | USER_ID | +----+-----------+---------+ | 1 | CUSTOMER1 | 1 | | 2 | CUSTOMER2 | NULL | | 3 | CUSTOMER3 | 3 | | 4 | CUSTOMER4 | 2 | | 5 | CUSTOMER5 | 2 | +----+-----------+---------+ 5 rows in set (0.00 sec) mysql> select * from Samples.USER; +----+-------+ | ID | NAME | +----+-------+ | 1 | USER1 | | 2 | USER2 | | 3 | USER3 | +----+-------+ 3 rows in set (0.00 sec) mysql> **HQLで検索する [#vf487710] ***通常通りやってみる [#l6bef2aa] まずはHQLで検索してみます。 public static void main(String[] args) { DOMConfigurator.configure("log4j.xml"); try { final SessionFactory sessionFactory = getSessionFactory(); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery("from Customer"); List models = query.list(); Iterator it = models.iterator(); while (it.hasNext()) { System.out.println(it.next()); } session.flush(); session.close(); } catch (Throwable e) { e.printStackTrace(); } } 結果は以下の通り。 org.hibernate.SQL - select customer0_.ID as ID1_, customer0_.USER_ID as USER2_1_, customer0_.NAME as NAME1_ from Samples.CUSTOMER customer0_ org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@1d439fe[id=1,name=CUSTOMER1,担当者=nu.mine.kino.entity.User@1e78c96[id=1,name=USER1]] nu.mine.kino.entity.Customer@148f8c8[id=2,name=CUSTOMER2,担当者=<null>] org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@1c5466b[id=3,name=CUSTOMER3,担当者=nu.mine.kino.entity.User@c2ee15[id=3,name=USER3]] org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@922804[id=4,name=CUSTOMER4,担当者=nu.mine.kino.entity.User@1d0d45b[id=2,name=USER2]] nu.mine.kino.entity.Customer@1815338[id=5,name=CUSTOMER5,担当者=nu.mine.kino.entity.User@1d0d45b[id=2,name=USER2]] CUSTOMERが全件検索されて、その後に関連つくUSERが検索されています。 ***結合をHQLでやる(from句で) [#l8fd849c] Joinをhbmの設定を利用してやってると、デフォルトではSQL一発ではなくCustomerごとにUserを取ってくる模様。これだとパフォーマンス的に悲惨になりそうなので、HQLでつなげてみました。 public static void main(String[] args) { DOMConfigurator.configure("log4j.xml"); try { final SessionFactory sessionFactory = getSessionFactory(); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session .createQuery("from Customer as customer left join customer.user"); List models = query.list(); Iterator it = models.iterator(); while (it.hasNext()) { Object element = it.next(); Object[] objects = (Object[]) element; System.out.println(objects[0]); // 明示的にjoinすると System.out.println(objects[1]); // CustomerとUserの配列になる?? } session.flush(); session.close(); } catch (Throwable e) { e.printStackTrace(); } } 結果は以下の通り。 org.hibernate.SQL - select customer0_.ID as ID1_0_, user1_.ID as ID0_1_,customer0_.USER_ID as USER2_1_0_, customer0_.NAME as NAME1_0_,user1_.NAME as NAME0_1_ from Samples.CUSTOMER customer0_ left outer join Samples.USER user1_ on customer0_.USER_ID=user1_.ID nu.mine.kino.entity.Customer@17e845a[id=1,name=CUSTOMER1,担当者=nu.mine.kino.entity.User@1ba0f36[id=1,name=USER1]] nu.mine.kino.entity.User@1ba0f36[id=1,name=USER1] nu.mine.kino.entity.Customer@3caa4b[id=2,name=CUSTOMER2,担当者=<null>] null nu.mine.kino.entity.Customer@d0220c[id=3,name=CUSTOMER3,担当者=nu.mine.kino.entity.User@6b496d[id=3,name=USER3]] nu.mine.kino.entity.User@6b496d[id=3,name=USER3] nu.mine.kino.entity.Customer@1a19458[id=4,name=CUSTOMER4,担当者=nu.mine.kino.entity.User@1124746[id=2,name=USER2]] nu.mine.kino.entity.User@1124746[id=2,name=USER2] nu.mine.kino.entity.Customer@105691e[id=5,name=CUSTOMER5,担当者=nu.mine.kino.entity.User@1124746[id=2,name=USER2]] nu.mine.kino.entity.User@1124746[id=2,name=USER2] SQL内でOuter Joinしてくれるようになりました。しかし戻りが配列になってるのがいかんですねぇ。 ちなみにleft joinを ただの joinと書くと内部結合になります。つまりUSER_IDがNULLのCUSTOMERは検索されません。 ***配列のところを改善する [#fd63deb5] 先のクエリ Query query = session .createQuery("from Customer as customer left join customer.user"); では返り値がCustomer,Userの配列(のリスト)となっていました。これをCustomerだけを返すようにするには以下のようにクエリを変更すればOKです。 Query query = session .createQuery("select customer from Customer as customer left join customer.user"); このようにselect [クラスのエイリアス名] とすれば、指定したクラスだけを返すことができます。結果は以下の通り。 org.hibernate.SQL - select customer0_.ID as ID1_, customer0_.USER_ID as USER2_1_, customer0_.NAME as NAME1_ from Samples.CUSTOMER customer0_ left outer join Samples.USER user1_ on customer0_.USER_ID=user1_.ID org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@1815338[id=1,name=CUSTOMER1,担当者=nu.mine.kino.entity.User@16917ee[id=1,name=USER1]] nu.mine.kino.entity.Customer@12368df[id=2,name=CUSTOMER2,担当者=<null>] DEBUG org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@1ba0f36[id=3,name=CUSTOMER3,担当者=nu.mine.kino.entity.User@1d0d45b[id=3,name=USER3]] DEBUG org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@3caa4b[id=4,name=CUSTOMER4,担当者=nu.mine.kino.entity.User@11db6bb[id=2,name=USER2]] nu.mine.kino.entity.Customer@d0220c[id=5,name=CUSTOMER5,担当者=nu.mine.kino.entity.User@11db6bb[id=2,name=USER2]] 確かに戻り値が配列ではなくなったのですが、selectがまた乱発されてしまった。。。うーん。 ***配列のところを改善する、かつフェッチ済み [#a5509f31] Query query = session .createQuery("select customer from Customer as customer left join customer.user"); だとCustomerから参照されているUserを参照したときにselectが発生してまずい状態になってました。すでにフェッチ済みにするには以下のようにすればOKです。 Query query = session .createQuery("select customer from Customer as customer left join fetch customer.user"); 結果は以下の通り。 org.hibernate.SQL - select customer0_.ID as ID1_0_, user1_.ID as ID0_1_, customer0_.USER_ID as USER2_1_0_, customer0_.NAME as NAME1_0_, user1_.NAME as NAME0_1_ from Samples.CUSTOMER customer0_ left outer join Samples.USER user1_ on customer0_.USER_ID=user1_.ID nu.mine.kino.entity.Customer@17e845a[id=1,name=CUSTOMER1,担当者=nu.mine.kino.entity.User@1ba0f36[id=1,name=USER1]] nu.mine.kino.entity.Customer@3caa4b[id=2,name=CUSTOMER2,担当者=<null>] nu.mine.kino.entity.Customer@d0220c[id=3,name=CUSTOMER3,担当者=nu.mine.kino.entity.User@6b496d[id=3,name=USER3]] nu.mine.kino.entity.Customer@1a19458[id=4,name=CUSTOMER4,担当者=nu.mine.kino.entity.User@1124746[id=2,name=USER2]] nu.mine.kino.entity.Customer@105691e[id=5,name=CUSTOMER5,担当者=nu.mine.kino.entity.User@1124746[id=2,name=USER2]] 完璧です。。。 ***結合をHQLでやる(where句で) [#a03858b1] 次にwhere句で結合します。 Query query = session .createQuery("from Customer as customer,User as user where customer.user.id=user.id"); これの結果はCustomer,Userの配列です。 org.hibernate.SQL - select customer0_.ID as ID1_0_, user1_.ID as ID0_1_, customer0_.USER_ID as USER2_1_0_, customer0_.NAME as NAME1_0_, user1_.NAME as NAME0_1_ from Samples.CUSTOMER customer0_, Samples.USER user1_ where customer0_.USER_ID=user1_.ID nu.mine.kino.entity.Customer@3caa4b[id=1,name=CUSTOMER1,担当者=nu.mine.kino.entity.User@6b496d[id=1,name=USER1]] nu.mine.kino.entity.Customer@1a19458[id=3,name=CUSTOMER3,担当者=nu.mine.kino.entity.User@1124746[id=3,name=USER3]] nu.mine.kino.entity.Customer@105691e[id=4,name=CUSTOMER4,担当者=nu.mine.kino.entity.User@383118[id=2,name=USER2]] nu.mine.kino.entity.Customer@11f23e5[id=5,name=CUSTOMER5,担当者=nu.mine.kino.entity.User@383118[id=2,name=USER2]] だんだんSQLみたいになってきました。これにselect句をつけてCustomerだけを取得することもできますが、例によってselectが乱発されます。またこれでは内部結合なので、外部結合にする方法をさがしたのですが[[HIBERNATE イン アクション 7.3.5 シータスタイル結合>http://www.amazon.co.jp/dp/4797330805?tag=kinosite-22&camp=23&creative=203&linkCode=st1&creativeASIN=4797330805&adid=1J4NH5NEXZF5RRMP8TWG&]] の項目によると「Hibernateはマッピングされた関連を持たない2テーブルを外部結合できない」となっていますね。。へぇ。。 **Criteriaで検索する [#g0e8b0dd] ***通常通りやってみる [#a959f42c] 次にCriteriaを使って検索します。 public static void main(String[] args) { DOMConfigurator.configure("log4j.xml"); try { final SessionFactory sessionFactory = getSessionFactory(); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Criteria criteria = session.createCriteria(Customer.class); // criteria.setFetchMode("user", FetchMode.JOIN); <-フェッチの指定 List models = criteria.list(); Iterator it = models.iterator(); while (it.hasNext()) { System.out.println(it.next()); } session.flush(); session.close(); } catch (Throwable e) { e.printStackTrace(); } } 結果は以下の通り。 org.hibernate.SQL - select this_.ID as ID1_0_, this_.USER_ID as USER2_1_0_, this_.NAME as NAME1_0_ from Samples.CUSTOMER this_ org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@32060c[id=1,name=CUSTOMER1,担当者=nu.mine.kino.entity.User@1e1be92[id=1,name=USER1]] nu.mine.kino.entity.Customer@1efb4be[id=2,name=CUSTOMER2,担当者=<null>] org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@435a3a[id=3,name=CUSTOMER3,担当者=nu.mine.kino.entity.User@1860038[id=3,name=USER3]] org.hibernate.SQL - select user0_.ID as ID0_0_, user0_.NAME as NAME0_0_ from Samples.USER user0_ where user0_.ID=? nu.mine.kino.entity.Customer@1d8c528[id=4,name=CUSTOMER4,担当者=nu.mine.kino.entity.User@1b33a0e[id=2,name=USER2]] nu.mine.kino.entity.Customer@77eaf8[id=5,name=CUSTOMER5,担当者=nu.mine.kino.entity.User@1b33a0e[id=2,name=USER2]] やっぱりCUSTOMERが全件検索されて、その後に関連付くUSERが検索されています。 ***フェッチの指定をする [#r1fc338e] コメントアウトしていた criteria.setFetchMode("user", FetchMode.JOIN); <-フェッチの指定 をアンコメントしてフェッチのモードを変更してやってみます。結果は以下の通り。 org.hibernate.SQL - select this_.ID as ID1_1_, this_.USER_ID as USER2_1_1_, this_.NAME as NAME1_1_, user2_.ID as ID0_0_, user2_.NAME as NAME0_0_ from Samples.CUSTOMER this_ left outer join Samples.USER user2_ on this_.USER_ID=user2_.ID nu.mine.kino.entity.Customer@e35bb7[id=1,name=CUSTOMER1,担当者=nu.mine.kino.entity.User@1d8c528[id=1,name=USER1]] nu.mine.kino.entity.Customer@9a8a68[id=2,name=CUSTOMER2,担当者=<null>] nu.mine.kino.entity.Customer@1038de7[id=3,name=CUSTOMER3,担当者=nu.mine.kino.entity.User@1f4e571[id=3,name=USER3]] nu.mine.kino.entity.Customer@183e7de[id=4,name=CUSTOMER4,担当者=nu.mine.kino.entity.User@5976c2[id=2,name=USER2]] nu.mine.kino.entity.Customer@10fe2b9[id=5,name=CUSTOMER5,担当者=nu.mine.kino.entity.User@5976c2[id=2,name=USER2]] あらかじめOuter Joinして検索してくれています。 -[[HIBERNATE イン アクション 7.6.1 n+1セレクト問題の解決>http://www.amazon.co.jp/dp/4797330805?tag=kinosite-22&camp=23&creative=203&linkCode=st1&creativeASIN=4797330805&adid=1J4NH5NEXZF5RRMP8TWG&]] **HQLいろいろ [#fcacc0af] ***サイズ指定 [#tf9fed02] Query query = session .createQuery("from Customer customer left join fetch customer.user user where size(user.customers) > 1"); とかで、複数の顧客を抱える担当者を検索できます。 from Customer customer left join fetch customer.user user で -Customerクラスを返します -customer.userを使って外部結合します。 -あらかじめUserクラスをfetchしておきます。 となります。で、 where size(user.customers) > 1 で、UserクラスのCustomersのコレクション(Set)のサイズが1以上のもの、を検索します。 実行結果は以下の通り。 org.hibernate.SQL - select customer0_.ID as ID1_0_, user1_.ID as ID0_1_, customer0_.USER_ID as USER2_1_0_, customer0_.NAME as NAME1_0_, user1_.NAME as NAME0_1_ from Samples.CUSTOMER customer0_ left outer join Samples.USER user1_ on customer0_.USER_ID=user1_.ID where ( select count(customers2_.USER_ID) from Samples.CUSTOMER customers2_ where user1_.ID=customers2_.USER_ID )> 1 nu.mine.kino.entity.Customer@16dc861[id=4,name=CUSTOMER4,担当者=nu.mine.kino.entity.User@2d189c[id=2,name=USER2]] nu.mine.kino.entity.Customer@aae86e[id=5,name=CUSTOMER5,担当者=nu.mine.kino.entity.User@2d189c[id=2,name=USER2]] なるほど。。 -[[Hibernate 入門記 問い合せ その3 式いろいろ>http://d.hatena.ne.jp/koichik/20040830#1093889012]] ***検索条件にプロパティを使う [#n6787da1] 下のように検索条件としてname=xxxってあるときgetName()というGetterをもつ任意のクラスをsetPropertiesで渡すことによって、検索条件を代入することができます。 user = (User) session.load(User.class, user.getId()); Query query = session.createFilter(user.getCustomers(), "where this.name=:name"); CustomerModoki modoki = new CustomerModoki(); modoki.setName("顧客1"); query.setProperties(modoki); List list = query.list(); class CustomerModoki { private String name; public String getName() {return name;} public void setName(String name) {this.name = name;} } この場合は名前が"顧客1"という検索を行うことができます。 **ちなみにgetSessionFactory()ってこんな内容 [#jf4a9283] private static SessionFactory getSessionFactory() { Configuration config = new Configuration(); return config.configure(new File("hibernate.cfg.xml")) .buildSessionFactory(); } **ちなみにInsertってすぐに行われるワケじゃない [#e1642958] データをインサートして、その後検索するってばあい、DBに実際にデータが挿入されるのは検索の直前みたい。たとえば上のテーブルに対して Customer c1 = new Customer(); c1.setName("顧客1"); customerDao.create(c1); System.out.println("検索を開始します"); Customer[] customers = customerDao.readAll(); for (Customer customer : customers) { System.out.println(customer); } ってやった場合((Daoとかは示してしてないけどまあ、普通のDaoです)) Hibernate: select max(ID) from CUSTOMER -----------------------------onSave 検索を開始します -----------------------------preFlush Hibernate: insert into CUSTOMER (USER_ID, NAME, ID) values (?, ?, ?) -----------------------------postFlush Hibernate: select this_.ID as ID1_0_, this_.USER_ID as USER2_1_0_, this_.NAME as NAME1_0_ from CUSTOMER this_ nu.mine.kino.entity.Customer@76627662[id=1,name=顧客1,担当者=<null>] org.springframework.orm.hibernate3.HibernateTransactionManager - Triggering beforeCompletion synchronization org.springframework.orm.hibernate3.HibernateTransactionManager - Initiating transaction rollback org.springframework.orm.hibernate3.HibernateTransactionManager - Rolling back Hibernate transaction on Session [org.hibernate.impl.SessionImpl@519e519e] -----------------------------afterTransactionCompletion org.springframework.orm.hibernate3.HibernateTransactionManager - Triggering afterCompletion synchronization org.springframework.orm.hibernate3.HibernateTransactionManager - Closing Hibernate Session [org.hibernate.impl.SessionImpl@519e519e] after transaction org.springframework.orm.hibernate3.SessionFactoryUtils - Closing Hibernate Session nu.mine.kino.dao.CustomerDaoTest - Rolled back transaction after test execution データを挿入した後に「検索を開始します」と書きましたが、実際はその後にInsert文が実行されていることが分かります。 ---- この記事は #vote(おもしろかった[2],そうでもない[1]) -結合はhbm.xmlに書いておけばHQLのクエリに書く必要はないみたいだな。結合はhbm.xmlで指定しておいて、いちいちHQLクエリには書かない、が基本だなぁ。。 -- [[きの]] &new{2006-11-12 18:39:44 (日)}; -[[HIBERNATE イン アクション 7.3.1 Hibernateのjoinオプション>http://www.amazon.co.jp/dp/4797330805?tag=kinosite-22&camp=23&creative=203&linkCode=st1&creativeASIN=4797330805&adid=1J4NH5NEXZF5RRMP8TWG&]] -- [[きの]] &new{2006-11-12 18:41:19 (日)}; -関連づけのないテーブル同士のOuter Joinはできないのか。。 -- [[きの]] &new{2006-11-12 21:59:44 (日)}; -通常From句でJoinすると配列が返ってくるけれど、fetchって書いておくとCustomerが返ってくるなぁ。。 -- [[きの]] &new{2006-11-12 23:51:03 (日)}; #comment #topicpath SIZE(10){現在のアクセス:&counter;}