Tomcat6.x系でDataSourceを使用してDBにアクセスしてみた。

ネットでいろいろ探しながらDataSourceを使用しました。
環境は以下です。
Eclipse3.4
Tomcat6.0.20


Tomcat5.x系の解説が多いので、それを見ながら実装しましたが、
以下のエラーがずっと解決できませんでした。

org.apache.tomcat.dbcp.dbcp.SQLNestedException:
Cannot create JDBC driver of class '' 
for connect URL 'null'

今日やっと解決できました。
結論、Tomcatのバージョンで配置するファイルの場所が違うことに気づきました。
「百聞は一見にしかず!!」ということで早速紹介します。
では今日の材料は以下になります。

<一人前>
Servlet・・・・1つ
Dto・・・・1つ
Web.xml・・・・少々
画面・・・・少々
Context.xml・・・・一つ

では吟じて見たいと思います。
吟じます!!
まずServletを書きます。
全体は以下のようになります。

package jp.net.damePg;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

public class DataSourseServlet extends HttpServlet {

	private Connection con = null;
	
	@Override
	public void init() throws ServletException {
		try {
			InitialContext ic = new InitialContext();
			DataSource ds = (DataSource)ic.lookup("java:comp/env/damePg");
			con = ds.getConnection();
		} catch (NamingException e) {
			// InitialContext生成時に例外発生
			e.printStackTrace();
		} catch (SQLException e) {
			// コネクション取得時に例外発生
			e.printStackTrace();
		}
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		if (con != null) {
			try {
				Statement st = con.createStatement();
				ResultSet rs = st.executeQuery("SELECT * FROM TEST");
				List list = new ArrayList();
				while(rs.next()) {
					Dto d = new Dto();
					d.setId(rs.getInt(1));
					d.setName(rs.getString(2));
					list.add(d);
				}
				req.setAttribute("SQLResult", list);
				rs.close();
				st.close();
				req.getRequestDispatcher("result.jsp").forward(req, resp);
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				try {
					con.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		} else {
			req.getRequestDispatcher("error.jsp").forward(req, resp);
		}
	}
}

GenericServletで実装されているinitをOverrideします。
そのなかでConnectionを取得するコードを書いています。
Connectionを取得しているのは以下です。

InitialContext ic = new InitialContext();
DataSource ds = (DataSource)ic.lookup("java:comp/env/damePg");
con = ds.getConnection();

Connectionを取得するという責務は、DriverManagerを使用するのと同じです。
少し解説しておきます。
lookupの引数のjava:comp/env/までは決まりです。
その後の「damePg」は後に記述するWeb.xmlとContext.xmlと一致しないといけません。


次にWeb.xmlです

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>datasourseTest</display-name>

	<servlet>
		<servlet-name>test</servlet-name>
		<servlet-class>jp.net.damePg.DataSourseServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>test</servlet-name>
		<url-pattern>/test</url-pattern>
	</servlet-mapping>
	
	<resource-ref>
		<res-ref-name>damePg</res-ref-name>
		<res-type>javax.sql.DataSource</res-type>
		<res-auth>Container</res-auth>
	</resource-ref>
</web-app>

通常のアプリケーション作成に加えて以下が必要になります。

<resource-ref>
	<res-ref-name>damePg</res-ref-name>
	<res-type>javax.sql.DataSource</res-type>
	<res-auth>Container</res-auth>
</resource-ref>

res-ref-nameはServlet、lookupの引数java:comp/env/の後に記述した内容と同じものを記述します。
res-authはContainerかApllicationを選択できます。
今回はオーソドックスにContainerを使用します。

では次にContext.xmlです。
ここが一番肝心です。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Resource 	name="damePg"
		auth="Container" 
		type="javax.sql.DataSource"
               	maxActive="100" 
		maxIdle="30" 
		maxWait="10000"
               	username="ユーザー名" 
		password="パスワード" 
		driverClassName="org.gjt.mm.mysql.Driver"
              	url="jdbc:mysql://localhost:3306/test?autoReconnect=true"/>
</Context>

「name」「auth」「type」はWeb.xmlで記述した内容と同じものを記述します。
続いて以下を記述します。
username・・・MySQLユーザ名
password・・・MySQLパスワード
driverClassName・・・DriverManagerを使用するときClass.forNameの引数に指定するのと同じもの
url・・・DriverManagerを使用するときDriverManagerのgetConnectionの第一引数に指定するのと同じもの


そしてContext.xmlをMETA-INF直下に配置します
20090621144552

Tomcat5.x系はServer.xmlに記述する方法がネットにはたくさんありますが
それではTomcat6.x系はExceptionが発生します。


後は普通にWebアプリケーションを開発するようにすればOK!!
ためしに動作させてみます。

まず、以下のようにDBに値を登録します。
20090621144553

その後、Servletにリクエストすると・・・・
20090621144551

出力しました。
あると思います!!

せっかくなのでこんな実験をしてみました。
mainメソッドで呼び出せるかどうかです。
以下のコードを実行します。

package jp.net.damePg;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.naming.InitialContext;
import javax.sql.DataSource;

public class DatasourceMain{

	public static void main(String[] args) {
		
		try {
			InitialContext context = new InitialContext();
			DataSource ds = (DataSource)context.lookup("java:comp/env/jdbc/datasource");
			Connection con =  ds.getConnection();
			Statement st = con.createStatement();
			ResultSet rs = st.executeQuery("select * from test");
			while (rs.next()) {
				System.out.println(rs.getInt("id"));
				System.out.println(rs.getString("name"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

すると以下の例外が発生しました。

javax.naming.NoInitialContextException: 
Need to specify class name in environment or system property,
or as an applet parameter,
or in an application resource file:
java.naming.factory.initial

DataSourcceはWebアプリケーションしか使用できないのですね。
松坂大輔の不調の原因は制球力だと思うdamePGでした。