はじめてのEJB 3.1 データベース編
足し算とかエコーとかデータベースアクセスを伴わないEJB3/3.1は何度も何度もサンプルとして書いた。データベースアクセスがあるとしてもJPAでさくっと。
今回はEJBを知らない人のためにデータベースアクセスを書いてみる。それを呼び出すクライアントはServlet。余分な基礎知識が要らないためだ。また、データベースアクセスには生JDBCを使う。これも余分な知識を必要としないため説明にはうってつけだろう。面倒ならばラッパを作ればいいだけなのでおいらはJDBCは結構嫌いではない。
特定の人の年齢のフィールドをインクリメントしつつ文字列を生成するEJB。以下のソースはインポートも全て省略なしですべて含んでいます!
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.annotation.Resource; import javax.ejb.Singleton; import javax.sql.DataSource; @Singleton public class HogeEJB { @Resource(mappedName="jdbc/sample") DataSource ds; public String なんかしゃべれ() { try { Connection con = ds.getConnection(); try{ String sql = "select * from test where id = 1 "; Statement st = con.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE ); ResultSet rs = st.executeQuery(sql); if(rs.next()){ String name = rs.getString("name"); int age = rs.getInt("age"); age++; rs.updateInt("age", age); rs.updateRow(); if(true){ // throw new RuntimeException("よきせぬれいがいがはっせい"); } return name + "は" +age+"さい"; }else{ //データあるのでここにはこないけど return "だれもいない マハマンするならいまのうち"; } }finally{ con.close(); } } catch (SQLException ex) { throw new RuntimeException("ここはさすがに生JDBCでも自動でろーるばっくされるよ"); } } }
動きは生JDBCなのでよくわかると思う。日本語メソッド万歳。
続いてクライアント側。
import java.io.IOException; import java.io.PrintWriter; import javax.ejb.EJB; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name="HogeServlet", urlPatterns={"/HogeServlet"}) public class HogeServlet extends HttpServlet { @EJB HogeEJB ejb; protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { out.println("<html>"); out.println("<head>"); out.println("<title>Servlet HogeServlet</title>"); out.println("</head>"); out.println("<body>"); out.println(ejb.なんかしゃべれ()); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override public String getServletInfo() { return "Short description"; } }
結果は予想通り。
EJBの例外を発行する部分のコメント部分をはずすと自動でロールバックします。
まとめ
設定ファイルは一切ありません。上の2つのファイルが本当に全てです。Servlet 3.0つかっているのでweb.xmlすら存在しません(空っぽではなくてファイルそのものがなくてよい)。
EJBは基本なにもしないで、トランザクションを開始して例外がなければコミット、例外があればロールバックします。
今回はやっていませんがAOPを設定することも可能です。アノテーションを利用してコードで書くほかに、設定ファイルに書いて複数のクラスにまとめて設定してあげることも可能です。
コネクションの取得とクローズを自分でしているのにトランザクション管理下におかれているのが不思議かもしれません。これがJTAです。closeのタイミングで物理的にcloseされるわけではないのです。コネクション管理をトランザクション管理から離れて考えることが出来るというのは非常に強力で開発を容易にします。
データソースはアノテーションで注入しています。コネクション数やプールの動的な数の増減などの調整等はアプリケーションサーバー側に設定をしてあげるだけです。つまり、運用時と開発時とでバイナリは変更されません。
EJB 3.1 liteにて書いていますが、ローカルインターフェースが省略できるようになったこと、warアーカイブ内にいれれるようになったいうだけで、Java EE 5時代のEJB 3.0となんら変化はありません。EJB3を食わず嫌いだったという人も多いのではないでしょうか?
これにリモートインターフェースを書くと他のマシンからのアクセスが出来るようになります。分散処理ですね。EJBはWebに閉じた技術ではないのです。それをWebよりにした(アーカイブの場所とか)のがEJB liteです。HTTPリクエストスコープとかHTTPセッションスコープがないのはそのためです。
とりあえずEJB使うかどうかはともかく、アプリケーションサーバーでデータベースのコネクションとトランザクション周りとサーブレットコンテナだけ使うようにしてみてはいかがでしょうか。JBOSSにしろGlassfishにしろサーブレットコンテナはみんな大好きTomcatベースです。そもそもTomcat自体Sunからのソースの寄贈で成り立ってますのでGlassfishがTomcatベースでもなんらおかしくないですが…。