第23夜 JavaSE5対応したDBUtilsを試す

DBUtilsはDBアクセス用ライブラリ。
非常にシンプルでDataSourceやConnectionがあればさくっと使えるのが魅力。


最近はO/Rマッピングが主流で、Hibernate/JPAにあらずんばDBアクセスにあらずという状況。似たようなのにSpringJDBCもあるが、こちらもほとんど改良されないまま。ほぼ終わった製品に近い。

日本国内は状況は違うようなので、ある意味日本向けのライブラリだと思う。


JavaSE 1.4対応していた時代ではそれなりに有効だったが、Commons自体5.0にほとんどの製品がいけてない。DBUtilsもそういう製品だった。2006年から放置。それがやっと去年動き出して、去年の末には対応した。

超がつくほど非常に小さなライブラリなので、バグがあったとしてもソースを修正するのは容易。依存ライブラリも確かゼロ(ロギングも必要なし)ということでコレを使わない手はない。


というわけでなつかしのDBUtilsを触ってみる。
http://commons.apache.org/dbutils/index.html


待望のジェネリクス対応と可変長引数対応。もっともこのフレームワークとほぼ同様のものは6年前においらはつくって使ってたけど、共通化されるのがよい。可変長引数対応に伴ってパラメータ引数(Objectのやつね)の順番が変わったこと、従来のObjectを引数に取るやつが非推奨になったことに注意だ。


サンプルとしてわかりやすいようにするために、EJBCDIを利用する。JavaEE 6ではEJBPOJOを注入できるのでトランザクション管理をEJBにさせて、DAOは単体テストがしやすいようにPOJOCDIを使う(EJBも同様に単体テストはしやすいけど)というのは一般的かと。AOPの設定も何もいらないし。トランザクション境界もわかりやすいし。何より標準APIなため、みんな知ってる確率が高い。2006年登場のEJB 3.0は常識としてみんな知っていると思うので、そこからEJB 3.1へは非常に楽に進めるはず。CDIよりは楽なはず。


DAOをEJBにするとデプロイ時間が増えやすいのでこういうのはありだと思う。まぁ今回はすべてEJBでやってもいいけどね。@Singletonつけて注入に@EJBつけるくらいしか違いはないし。

マップするDTO

public class Customer {

    Integer id;
    String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


続いてDAO。ここでDBUtil使ってる。単体テストしやすいようにセッターインジェクションにするのもあり。もしくはフィールドのデータソースをprivate以外にするか、DIやファクトリから取得するようにするかでもいいけど。

public class CustomerDAO {

    @Resource(name = "jdbc/sample")
    DataSource dataSource;

//    @Resource(name = "jdbc/sample")
//    public void setDataSource(DataSource dataSource) {
//        this.dataSource = dataSource;
//    }

    public Customer findById(Integer id) {
        QueryRunner runner = new QueryRunner(dataSource);
        String sql = "select customer_id as id, name "
                + "from customer "
                + "where customer_id = ?";
        ResultSetHandler<Customer> handler = new BeanHandler<Customer>(Customer.class);
        try {
            return runner.query(sql, handler, id);
        } catch (SQLException ex) {
            throw new RuntimeException("エラーよ", ex);
        }
    }
}

EJB。DAOを注入。EJB3.1ではインターフェースは必要なし+シングルトン追加。

@Singleton
public class CustomerService {

    @Inject
    CustomerDAO dao;

    public Customer find(int id){
        return dao.findById(id);
    }

}


EJBを利用するサーブレット。@EJBで注入は3.0とかわらず。

@WebServlet(name="CustomerServlet", urlPatterns={"/customer"})
public class CustomerServlet extends HttpServlet {
   
    @EJB
    CustomerService service;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {

            Customer c = service.find(2);//IDで検索
            service.updateName(c);

            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet CustomerServlet</title>");
            out.println("</head>");
            out.println("<body>");
            out.printf("<h1>%d : %s</h1>" ,c.getId(), c.getName());
            out.println("</body>");
            out.println("</html>");
        } finally {
            out.flush();
        }
    } 
}

ブラウザで開くと検索した結果が表示されます。当たり前ですけど。

原理的にはまりがないので生SQL+Beanへのマップだけを使う場合は一番おすすめしたい。コレを下敷きにしてデータアクセスフレームワークを自作でも良いだろう。ManyToOneとか関連を追うようなアプリならHibernateなどJPA系で。それ以外でフラットに結果セットをつめるだけならこっち系で。

フレームワークっちゅうものはなんでも用意されたレールから外れたとき地獄を見るので、レールから外れないタイプのアプリなのか、外れる可能性があるならカスタマイズどれだけできるかを見極めるのが大事。