Kohei Nozaki's blog 

@ResourceアノテーションでDataSourceを注入してみる


Posted on Sunday Feb 02, 2014 at 05:23PM in Technology


直接JDBCを使う必要がでてきた時用。

環境

  • WildFly8.0.0.CR1
  • Oracle JDK7u51
  • PostgreSQL 9.2.4

準備

データソースを確認

こんなデータソースを使ってみることにする

[standalone@localhost:9990 /] /subsystem=datasources/data-source=TestDS:read-attribute(name=jndi-name)
{
    "outcome" => "success",
    "result" => "java:jboss/jdbc/TestDS"
}
[standalone@localhost:9990 /] 

HogeServlet.java

このサーブレットにDataSourceを注入します。try-with-resourcesが便利ですね。@Resourceアノテーションのlookup属性には、“java:comp/env/“の後にXMLファイルで定義した論理名を書く

package com.example;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

@WebServlet("/HogeServlet")
public class HogeServlet extends HttpServlet {

    @Resource(lookup = "java:comp/env/jdbc/hoge")
    DataSource dataSource;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try(PrintWriter pw = response.getWriter();
            Connection cn = dataSource.getConnection();
            Statement st = cn.createStatement();
            ResultSet rs = st.executeQuery("select now()")){
            while(rs.next()){
                pw.write(String.valueOf(rs.getTimestamp(1)));
            }
        }catch(SQLException e){
            throw new ServletException(e);
        }
    }
}

web.xml

この環境の場合はなくても動くようだが一応。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <resource-ref>
        <res-ref-name>jdbc/hoge</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
    </resource-ref>
</web-app>

jboss-web.xml

WEB-INFの下に置く。ここでデータソースのJNDI名とResourceアノテーションのlookup属性で指定した名前のひも付けを行う。

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <resource-ref>
        <res-ref-name>jdbc/hoge</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <jndi-name>java:jboss/jdbc/TestDS</jndi-name>
    </resource-ref>
</jboss-web>

動かしてみる

テストの際はどうするか

jboss-web.xmlを別のものに差し替えればOK。HogeServletには手を入れずに参照するDBを切り替えることができる。

備考

JPAのpersistence.xmlからはweb.xmlとjboss-web.xmlで定義した論理名を参照できない

persistence.xmlからもweb.xmlとjboss-web.xmlで定義した論理名 “jdbc/hoge” を使う事が出来れば、jboss-web.xmlを差し替えるだけでJPAから参照するデータソースも切り替えられるので楽だなと思ったのだが、どうやらそれは出来ないらしい[2]。“java:comp/env/jdbc/hoge” とかも試してみたが駄目だった。persistence.xmlにはAPサーバに定義してあるJNDI名を直接指定してやる必要があるみたい。残念。WebSphereだとできたりするみたいだけど一般的には駄目っぽい[5]。

今回の例だとこう書いてやる必要がある。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="datasource">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:jboss/jdbc/TestDS</jta-data-source>
    </persistence-unit>
</persistence>

ただアプリケーションローカルでweb.xmlかアノテーションで定義したデータソースを使う場合は共用が可能らしい[3]。アプリの資源に接続情報べた書きになるので避けたい気もするが用途によっては良いのかもしれない。

@Resourceで直接JNDI名を参照する

特に論理名を通じて利用する必要がないなら、HogeServlet.javaで以下のようにすれば直接JNDI名からデータソースを拾って来れる。jboss-web.xmlもweb.xmlも不要になる。

ただし複数箇所でJNDI名の直書きが発生するのはあまりよろしくないので、CDIのProducerを使うか、前述の手段で論理名を通して参照した方が良いような気はする。

package com.example;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

@WebServlet("/HogeServlet")
public class HogeServlet extends HttpServlet {

    @Resource(lookup="java:jboss/jdbc/TestDS")
    DataSource dataSource;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try(PrintWriter pw = response.getWriter();
            Connection cn = dataSource.getConnection();
            Statement st = cn.createStatement();
            ResultSet rs = st.executeQuery("select now()")){
            while(rs.next()){
                pw.write(String.valueOf(rs.getTimestamp(1)));
            }
        }catch(SQLException e){
            throw new ServletException(e);
        }
    }
}

参考文献

  1. JBoss でのデータソースの定義のしかた (DB2/400)
  2. Beware of soapy frogs: Java EE 6 - Traps, pitfalls and warts list
  3. DataSource Resource Definition in Java EE 6 (Enterprise Tech Tips)
  4. Java EE 7 Deployment Descriptors | Antonio's Blog
  5. WebSphere Application Server Version 8 Information Center



No one has commented yet.

Leave a Comment

HTML Syntax: NOT allowed