Hibernate による O/R マッピング(その 3)
DAO クラスの作成
package jp.mydns.akanekodou.dao; import org.hibernate.SessionFactory; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.hibernate.cfg.Configuration; public class DaoUtil { private SessionFactory sessionFactory; private static DaoUtil inst = new DaoUtil(); private DaoUtil() { sessionFactory = createSessionFactory(); } public static SessionFactory getSessionFactory() { return inst.sessionFactory; } private SessionFactory createSessionFactory() { Configuration conf = new Configuration().configure(); ServiceRegistryBuilder sb = new ServiceRegistryBuilder(); sb.applySettings(conf.getProperties()); ServiceRegistry sr = sb.buildServiceRegistry(); return conf.buildSessionFactory(sr); } }
SessionFactory
はスレッドセーフなのでシングルトンにして使い回します。
package jp.mydns.akanekodou.dao; import org.hibernate.Session; import org.hibernate.Criteria; import java.util.List; import jp.mydns.akanekodou.model.Customer; public class CustomerDAO { private Session session; public CustomerDAO() { session = DaoUtil.getSessionFactory().openSession(); } public List<Customer> all() { Criteria cr = session.createCriteria(Customer.class); @SuppressWarnings("unchecked") List<Customer> result = cr.list(); return result; } public Customer find(int id) { return (Customer)session.get(Customer.class, id); } }
get
メソッドは主キーを指定してデータを取得するメソッドです。似たような働きをするものに load
メソッドがあります。get
メソッドと load
メソッドの違いとしては
get
メソッド
- メソッド呼び出し時に直ちに SQL 文が発行される
- 返り値は一時的なもので、これに変更を加えてもデータベースには反映されない
- session を close した後もアクセスできる
- 該当するデータがなければ
null
を返す
load
メソッド
- メソッド呼び出し時には SQL 文は発行されず、getter が呼び出されて初めて評価される(Lazy loading)
- 返り値は永続化されたオブジェクトであり、これに変更を加えるとデータベースにも反映される
- session を close 後、それ以前に実行していなかった getter を呼び出すと
LazyInitializationException
が throw される - 該当するデータがない場合でも
null
にはならないが、getter などでアクセスしようとするとObjectNotFoundException
が throw される
このように load
メソッドは極めて Hibernate らしい*1挙動をします。この場合は入力値に対応するデータが存在するとは限りませんので get
を使うことにします。
package jp.mydns.akanekodou.dao; import org.hibernate.Session; import org.hibernate.Query; import java.util.List; import jp.mydns.akanekodou.model.Product; public class ProductDAO { private Session session; public ProductDAO() { session = DaoUtil.getSessionFactory().openSession(); } public List<Product> like(String keyword) { Query query = session.createQuery("from Product where name like :keyword"); query.setString("keyword", "%" + keyword.replace("%", "\\%").replace("_", "\\%") + "%"); @SuppressWarnings("unchecked") List<Product> result = query.list(); return result; } }
HQL 文を用いてあいまい検索を実行しています。
Query query = session.createQuery("from Product where name like :keyword"); query.setString("keyword", "%" + keyword + "%");
の部分は非常に重要です。何故ならば、これをうっかり
Query query = session.createQuery("from Product where name like '%" + keyword + "%'");
のように書いてしまうととんでもないことになります。もし keyword
に
xxx';delete from Product;
という文字列が送られて来たらどうなると思いますか ? おそらくこの delete
文は実行されるでしょう。それによって大事なデータが全て消去されてしまうことになります。HQL インジェクションと言ってもいいセキュリティホールの出来上がりです。SQL が HQL になっても、書き方一つでセキュリティホールは出来上がってしまいます。ご注意を。後程 HQL 文を使わない方法をご紹介します。