Struts+Hibernateでデータベース接続まで時間が掛かる問題

相変わらず放置具合が素敵な感じになっていますが、それでもブログ運営を続けます(≧∇≦)
サンプルが書かれているサイトを参考に製作していたため、思わぬ落とし穴に気付かなかった話でも書いてみようかと思います。

Struts+HibernateのWebアプリケーションを製作していたら、データベースに接続するページがやけに遅い気がしました。そこで、実際にLog4Jを用いて計測してみたところ、データベースの接続(Hibernateのセッション接続)のところで0.5秒前後掛かってました。
これはいくらなんでも掛かりすぎだろ...

データベースのコネクションプールが問題なのかと思い、hibernate.cfg.xmlのhibernate.connection.pool_sizeの値を変更してみましたが、状況としては全く変わらずorz

しかし、調べてみると原因は思わぬところにありました。
例えば、何かの情報について検索するDAOに以下のようなメソッドを作成するとします。

public List searchAll() {
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
// 罠にはまった壁
List list = session.createCriteria(クラス名.class).list();
return list;
}
よくHibernateのサンプルを公開しているHibernate初心者向けサイトに行くと、メソッドごとに「//罠にはまった壁」から上の3行が書かれています。それが問題でした。 上の3行をメソッドごとに記述してしまうと、コネクションプールが設定されていたとしても、それを切断して新たなコネクションプールを作成してしまいます。コネクションプールを用いて接続までの時間を短縮させる設定が全く活かされなかったんですね。

ではどうするかと言いますと、「//罠にはまった壁」の上3行のうち、上位2行をプラグインとしてTomcat起動前に読み込ませる必要があります。

package hibernate;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class SamplePlugIn implements PlugIn {

public void init(ActionServlet servlet, ModuleConfig config)
throws ServletException {
// Webアプリケーションの開始処理
System.out.println("*** SamplePlugIn initメソッド 起動 ***");

// Hibernateのセッションを取得
Configuration hibernateConfig = new Configuration().configure();
SessionFactory sessionFactory = hibernateConfig.buildSessionFactory();

// メッセージをServletContextに保存
ServletContext context = servlet.getServletContext();
context.setAttribute("sessionFactory", sessionFactory);

System.out.println("*** SamplePlugIn initメソッド 終了 ***");
}

public void destroy() {
// Webアプリケーションの終了処理
System.out.println("*** SamplePlugIn destory()メソッド ***");
}
}


次に、DAOやStrutsのActionクラス内でServletContext(Appilcationスコープ)に格納した「sessionFactory」を取得し、セッションを作成します。

ServletContext context = servlet.getServletContext(); Session session = context.getAttribute("sessionFactory").openSession();
その後は、Hibernateによりデータベースの処理を記述するだけです。

これでHibernateに0.05〜0.1秒で接続するようになり、体感的に遅く感じなくなりました。

※上の例ではアプリケーションスコープに格納する方法を採用しましたが、init()メソッドにSessionFactoryの作成までを行うstaticメソッドを作成し、同様にHibernateのセッションを静的メソッドで行う方法もあるようです。
そんなわけで、めでたしめでたしということで。

※StrutsのActionでHiberateのセッションを取得したい方は、StrutsからServletContextクラスを取得するにはも参考にどうぞ。