- - PR -
session競合?NullPointer例外が発生します
1
投稿者 | 投稿内容 | ||||
---|---|---|---|---|---|
|
投稿日時: 2005-11-17 18:43
いつもお世話になります。開発環境:windowsXP(HOME) j2sdk1.4.2_06、
ECLIPSE3.0.1、postgreSQL8.0.4、struts1.2、HIBERNATE2.1.7 です。 HIBERNATEのマニュアルからそのままコピーしてるシングルトンクラスから sessionをもらってデータベース挿入処理と取り出す処理をさせて表示させる WEBアプリを作成してるところです。 今回、strutsアクションクラスで入力フォームからの情報をデータベースに 挿入する処理と、データベースの情報を一覧表示するlistをrequestにセットする 処理をさせたところ、NullPointerExceptionが発生してしまいます。 データベースから情報を取り出すときにエラーが発生するのですが、直前にDBへの 挿入処理があります。挿入処理をコメントアウトするとエラーは発生せずちゃんと 一覧表示画面へ移行することができます。 以下にソースをコピペします。 **** session受け渡し用シングルトンクラス ******** package test; import net.sf.hibernate.*; import net.sf.hibernate.cfg.*; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { // SessionFactory を作る sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (HibernateException ex) { throw new RuntimeException("Configuration problem: " + ex.getMessage(), ex); } } public static final ThreadLocal session = new ThreadLocal(); public static Session currentSession() throws HibernateException { Session s = (Session) session.get(); // このスレッドにまだ何もなければ、新しいセッションをオープンする if (s == null) { s = sessionFactory.openSession(); session.set(s); } return s; } public static void closeSession() throws HibernateException { Session s = (Session) session.get(); session.set(null); if (s != null) s.close(); } } ***** アクションクラス **************** package entity.hibernate; import java.util.List; import javax.servlet.http.*; import org.apache.struts.action.*; import entity.hibernate.access.PageColorDao; public final class PageColorAction extends Action { public ActionForward execute (ActionMapping map, ActionForm frm, HttpServletRequest request, HttpServletResponse response) { PageColorForm objFrm=(PageColorForm)frm; PageColorDao dao=new PageColorDao(); dao.insertPageColor( objFrm.getColorName(), objFrm.getCssFilePath(), "damie"); List list=dao.getList(); request.setAttribute("objFrm",objFrm); Object object=list.toArray(); request.setAttribute("pageColor.list", object); return map.findForward("success"); } } ***** DAOクラス ************* package entity.hibernate.access; import net.sf.hibernate.Session; import net.sf.hibernate.Transaction; import net.sf.hibernate.HibernateException; import java.util.List; import entity.hibernate.PageColor; public class PageColorDao { public void insertPageColor(java.lang.String colorName,java.lang.String cssFilePath,String damie) { try{ Session sess1 = test.HibernateUtil.currentSession(); Transaction tx = sess1.beginTransaction(); PageColor entityObj = new PageColor(); entityObj.setColorName(colorName); entityObj.setCssFilePath(cssFilePath); sess1.save(entityObj); tx.commit(); sess1.close(); } catch(HibernateException e){ e.printStackTrace(); } } public List getList(){ List list=null; try{ Session sess = test.HibernateUtil.currentSession(); list = sess.find("from PageColor"); sess.close(); } catch(HibernateException e){ //e.printStackTrace(); System.out.println(e.getMessage()); } return list; } } sessionが競合してるのでしょうか? DAOクラスの作成方法が間違ってるのでしょうか? 回避法等ご存知の方がいらっしゃいましたらご教示いただきたく お願いいたします。 | ||||
|
投稿日時: 2005-11-17 19:40
Hibernateは詳しくないのですが、ぞれぞれのメソッドでsessionをクローズしてしまっている
のが原因ではないですか? このままだと検索メソッドではクローズされているSessionオブジェ クトを受け取ってしまうようですが。 CurrentSessionに対応するのはCloseSessionのようなので、そちらを呼び出すべきなのでは? | ||||
|
投稿日時: 2005-11-17 22:02
ukさん、ご返答いただきありがとうございます。
まったくもっておっしゃるとおりでした。 HIBERNATEのマニュアルにもその旨かかれてあったのですが、 見落としていたのと普通のsessionと勘違いして使って しまってたのでした。修正後はちゃんと動作しております。 お騒がせしました。 自分がThreadLocalについていまいち理解できてないのがそもそもの原因 ということになるのでしょうか・・・。いろいろ調べてみたのですがなかなか 核の部分を理解できてないのです・・・。 結局ThreadLocalっていったいなんなんだろう・・・。 | ||||
|
投稿日時: 2005-11-18 00:36
平たく言うと、
キー値にThread.currentThread()でえられるオブジェクト、値にset() メソッドで与えた値、のペアをEntryとするHashMapです。 「Thread単位でのSingletonオブジェクト」を簡単に実現してくれます。 ThreadLocalのソースはsrc.zipにはいっていますので、のぞいてみると よいですよ。 | ||||
|
投稿日時: 2005-11-19 01:10
シュンさんご返答ありがとうございます。
アドバイスのとおりソースを見て、ちょっとWEBで周辺情報を 見てみたのですが、やっぱり理解には時間がかかりそうです。 自分的に理解しにくい部分として、 public static final ThreadLocal session = new ThreadLocal(); public static Session currentSession() throws HibernateException { Session s = (Session) session.get(); // このスレッドにまだ何もなければ、新しいセッションをオープンする if (s == null) { s = sessionFactory.openSession(); session.set(s); } return s; } public static void closeSession() throws HibernateException { Session s = (Session) session.get(); session.set(null); if (s != null) s.close(); } この二つのメソッドでセッションをもらう処理と閉じる処理をしてる わけで、複数のユーザーが違うセッションを使用していて、誰かが セッションを閉じる時にcloseSession()を呼び出しますよね? その時のsessionを識別するIDとかがcloseメソッドの引数になってない のにどうやってこのユーザーが開いたsessionだと判断するのか的な 部分が飲み込めていません。 せっかくソースを見てみる方法を教えていただいたのに理解力がなく 申し訳ありません。自分はまだソースを見ても理解できるレベルじゃない ようです・・・。もし理解を助ける方法等ご存知でしたらお手数ですがどうか 今一度ご教示いただきたくお願いいたします。 | ||||
|
投稿日時: 2005-11-19 17:34
こんな説明でよいですか?
サーバーサイドの処理は、通常リクエストをクライアントから受け取ってから、 クライアントにレスポンスを返すまでの一連の処理を、1本のスレッドが継続して 担当しているっていうのは、イメージできます? 「いまそのメソッドを実行しているスレッド」を表現するThreadオブジェクトは、 staticメソッドThread#currentThread()で、いつでもどこででも取得できます。 takeさんがおっしゃっている「sessionのID」を、このThreadオブジェクトが担当 している、といえばわかっていただけますか? リクエストスコープをまたがる処理について、どのようにセッションの識別するの かは、ちょっとそのソースでは分かりませんが、少なくとも1リクエスト単位であ れば、ThreadLocalで用件は満たせますね。 | ||||
|
投稿日時: 2005-11-20 01:27
なるほど。そういう仕組みだったのですね。今日本屋さんに行ってこの手の参考書が 無いか探してきたのですが見つけられませんでした。 でもここまで説明していただければ後は自分で少しずつでも理解していけそうです。 ありがとうございました。 |
1