MySQL5 のセットアップ
データベースエンジンにはいろいろありますが、とりあえず有名どころでフリーのMySQL を使ってみましょう。詳しいインストールはコチラを参照してください。
データベースとユーザーの新規作成
データベース名bbs_db を作成します。続いて操作用の一般ユーザーを作ります。ユーザー名は"user_bbs"、パスワードは"pass_bbs" とします。
/usr/local/mysql/bin/mysql -u root -p
create database bbs_db;
// ユーザーの作成
grant USAGE on *.* to user_bbs@ホスト名 identified by 'pass_bbs';
grant all privileges on bbs_db.* to user_bbs@ホスト名;
flush privileges;
exit;
まずグローバル権限を設定し、次にbbs_db に対する権限を設定しています。USAGE は権限無しで、all は全権限ありです。これで
データベースbbs_db にだけフルアクセスできるユーザーが出来ました。ホスト名にはMySQL にアクセスしてくるクライアントのそれを
指定します。同じコンピュータ内にあればlocalhost でいけそうですが(実際Windows ではOK)、Linux ではホスト名になってしまい
MySQL へのログインに失敗するので、しっかり指定します。セキュリティに不安がなければホスト名に'%'を指定して、全てのホストからの
接続を許可する選択も可能です。
DBCP の設定
Tomcat からJDBC を使ってデータベースに接続する為の設定をします。アクションクラスからDriverManager で直接JDBC ドライバを読み込んで
データベースに接続する事もできますが、Jakarta プロジェクトの一つであるDBCP (Data Base Connection Pooling?) を使うとありがちな
オーバーヘッドを抑えてくれてパフォーマンスが良いです。またデータベースの接続をTomcat サイドに行わせる事で、各Web アプリケーションにおいて
データベースの種類を意識しないコーディングが可能になります。
struts1.1 以前ではDataSource というデータベース用のリソースがありましたが、これは1.2 以降では非推奨となり削除されています。ユーザーは
自力で実装するか、DBCP といった別モノを使う必要があります。DataSource を利用したアプリやサンプル・本は多数ありましたが、一切お奨めできなくなりました。
それどころかstruts1.2 ではstruts-config.xml のDataSouces に何か書くだけでエラーになります。私もこれにハマりました:-<
JDBC ドライバ
まずは何といってもMySQL をJava から使う為のコネクタをインストールしておく必要があります。MySQL のDownload Connector/J 5.0から mysql-connector-java-5.0.5.tar.gz をダウンロードします。展開した中にあるmysql-connector-java-5.0.5-bin.jar だけを$CATALINA_HOME/common/lib にコピーします。このjar を環境変数
CLASSPATH に含める必要はありません。
cd /tmp
wget "http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.0.5.tar.gz/
from/http://mirror.mysql-partners-jp.biz/"
tar zxvf mysql-connector-java-5.0.5.tar.gz
cd mysql-connector-java-5.0.5
cp mysql-connector-java-5.0.5-bin.jar $CATALINA_HOME/lib
次いでDBCP はJakarta Commons プロジェクトからダウンロードします。同じプロジェクト中のCollections、pool、Logging にも依存してるので必須です。それぞれダウンロードしたら解凍して
$CATALINA_HOME/common/lib にjar ファイルをコピーしておきます。
cd /tmp
wget "http://www.meisei-u.ac.jp/mirror/apache/dist/jakarta/commons/\
dbcp/binaries/commons-dbcp-1.2.1.tar.gz"
tar zxvf commons-dbcp-1.2.1.tar.gz
cd commons-dbcp-1.2.1
cp commons-dbcp-1.2.1.jar $CATALINA_HOME/lib
cd /tmp
wget "http://sunsite.tus.ac.jp/pub/apache/jakarta/commons/\
collections/binaries/commons-collections-3.2.tar.gz"
tar zxvf commons-collections-3.2.tar.gz
cd commons-collections-3.2
cp commons-collections-3.2.jar $CATALINA_HOME/lib
cd /tmp
wget "http://sunsite.tus.ac.jp/pub/apache/jakarta/commons/\
pool/binaries/commons-pool-1.3.tar.gz"
tar zxvf commons-pool-1.3.tar.gz
cd commons-pool-1.3
cp commons-pool-1.3.jar $CATALINA_HOME/lib
cd /tmp
wget "http://sunsite.tus.ac.jp/pub/apache/jakarta/commons/\
logging/binaries/commons-logging-1.1.tar.gz"
tar zxvf commons-logging-1.1.tar.gz
cd commons-logging-1.1
cp commons-logging*.jar $CATALINA_HOME/lib
Tomcat のserver.xml を編集する
DBCP はTomcat で動くリソースなので、$CATALINA_HOME/conf/server.xml に記述します。各Web アプリケーションからは、このリソースを経由して
データベースに接続する事になります。GlobalNamingResources エレメントの下にResouce タグを新規作成します。
<GlobalNamingResources>
<!-- 追加 -->
<Resource name="MySQL_DBCP" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="user_bbs" password="pass_bbs"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://127.0.0.1:3306/bbs_db?autoReconnect=true&
useUnicode=true&characterEncoding=SJIS" />
username, password 属性にMySQL にログインする為のID、パスワードを設定します。
個別のアプリ専用の接続設定の場合
アプリサイドのコンテキストXML に直接リソースを設定する事もできます。当然、そのアプリ内のみでの
利用となります。
コンテキストXML ファイルを作成する。
server.xml のGlobalNamingResources エレメントに設定したリソースを各Web アプリから利用する為には、ResourceLink を用いてローカルにマッピングする必要が
あります。このContext 情報はserver.xml の中のHost エレメント内にも記述できますが、Tomcat5.5 では別ファイルで記述する事が推奨されています。
アプリの保存されているディレクトリ配下のMETA-INF ディレクトリにcontext.xml というファイルを用意します。
<Context path="/bbs05" docBase="bbs05" debug="0" reloadable="true">
<ResourceLink name="MySQL_DBCP" global="MySQL_DBCP" type="javax.sql.DataSource"/>
</Context>
これでWeb アプリのアクションクラスといったJava コード内からサーブレットコンテキストの属性MySQL_DBCP 名で利用できるようになります。
(逆にResourceLink でなくResource 自体を記述すれば、このアプリ内のみで利用可能なリソースを用意できます)
アプリケーションのweb.xml を編集
最期にアプリケーションの/WEB-INF/web.xml にも登録しておきます。<web-app> タグ中のラストに、server.xml に書いたResouce のname, auth, type 属性値を以下のように書きます。
<resource-ref>
<res-ref-name>MySQL_DBCP</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
これでWeb アプリのアクションクラスといったJava コード内からサーブレットコンテキストの属性MySQL_DBCP 名で利用できるようになります。
簡易掲示板を作る
ハンドルネームとコメント、日時をデータベースに保存してゆく簡易掲示板を作成します。データベースに連動させれば、
永続的な掲示板になります。
bbs.jsp を改良する
入力フォームとデータベースから一覧表示する二つがメインとなります。まずフォームにハンドルネームおよびコメント用のテキストボックスを設置します。
次いでデータベースに入力された過去のコメントを一覧出力するlogic:iterator 部を記述します。
<%@page contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<html:html locale="true">
<head>
<title>bbs.jsp</title>
<html:javascript formName="bbs_dvf" />
</head>
<body>
<div>
<html:form action="/bbs" method="POST" onsubmit="return validateBbs_dvf(this);">
<html:text property="name" size="10" />
<html:text property="comment" size="30" />
<html:submit property="submit" value="実行" />
</html:form>
</div>
<logic:notEmpty name="MemoList" scope="request" >
<table border="2">
<logic:iterate id="memotable" name="MemoList" scope="request">
<tr>
<td width="120"><bean:write name="memo" property="name" /></td>
<td width="400"><bean:write name="memo" property="comment" /></td>
<td width="180"><bean:write name="memo" property="time" /></td>
</tr>
</logic:iterate>
</table>
</logic:notEmpty>
</body>
</html:html>
最初のjsp リクエスト時にはアクションが実行されないので、当然リクエストスコープにMemoList インスタンスは
ありません。そこでlogic:iterator は例外を発するので、login:notEmpty を用いてチェックしてる訳です。なお
logic:iterate はコレクションオブジェクトにシーケンシャルアクセスする為のタグライブラリです。
struts-config.xml の修正
アクションフォームのプロパティがname, comment となりましたので、変更します。
<form-bean name="bbs_dvf" type="org.apache.struts.validator.DynaValidatorForm">
<form-property name="name" type="java.lang.String" />
<form-property name="comment" type="java.lang.String" />
</form-bean>
varidation.xml の修正
プロパティ名が変わったので、コチラも修正します。とりあえずアクションフォームのプロパティ名name に関して
required(入力チェック) とします。
<form-validation>
<formset>
<form name="bbs_dvf">
<field property="name" depends="required">
</field>
</form>
</formset>
</form-validation>
データベースとテーブルを準備する
MySQL 上に今回使うためのデータベースならびにテーブルを作成します。データベース名はbbs_db、テーブル名はmemotable です。テーブルの構成は
インデックス番号となるid、ハンドル名を格納するname、コメントを収めるcomment、そして作成日時が自動で収められるタイムスタンプtime です。
mysql というコマンドでコンソールに入って操作します。
mysql -u root -p
create database bbs_db;
use bbs_db;
create table memotable (
id int auto_increment,
name varchar(32),
comment text,
time timestamp,
index(id)
);
memotable クラスを作成する
データベースから取得したデータを格納する為のクラスです。C++ なら単に構造体で済ませるところですが、Java では何でもクラスです。
// Compile command. You must be current file's directry.
// javac -classpath "../lib/struts.jar;../../../../common/lib/servlet-api.jar;"
-d "." MemoTable.java
package mypackage;
import java.io.*;
import java.util.*;
import java.text.*;
public class MemoTable implements Serializable
{
private String m_strName = "";
private String m_strComment = "";
private String m_strTime = "";
public void setName( String value){ this.m_strName = value; }
public String getName(){ return this.m_strName; }
public void setComment( String value){ this.m_strComment = value; }
public String getComment(){ return this.m_strComment; }
public void setTime( String value){ this.m_strTime = value; }
public String getTime(){ return this.m_strTime; }
}
プロパティへのアクセスメソッドだけの単純なクラス。コンパイルしておきましょう。
bbsAction.java を改造する
アクションフォームに格納されたリクエストデータをデータベースに入力します。続いてデータベースより抽出した
過去のコメントをリクエストスコープに割り当てたコレクションオブジェクトに格納します。
// Compile command. You must be current file's directry.
// javac -classpath "../lib/struts.jar;../../../../common/lib/servlet-api.jar;"
-d "." bbsAction.java
package mypackage;
import mypackage.MemoTable;
import java.sql.*;
import java.util.*;
import javax.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.*;
import org.apache.struts.action.*;
import org.apache.struts.validator.*;
import java.io.*;
import javax.naming.*;
public class bbsAction extends Action
{
public ActionForward execute
(
ActionMapping mapping,
ActionForm form,
HttpServletRequest req,
HttpServletResponse res
)
throws Exception
{
InitialContext ic = new InitialContext();
DataSource refDataSource = (DataSource)ic.lookup("java:comp/env/MySQL_DBCP");
Connection refConnection = refDataSource.getConnection();
// データベースに登録
{
DynaValidatorForm myForm = (DynaValidatorForm)form;
String strName = (String)myForm.get("name");
String strComment = (String)myForm.get("comment");
String strSQL = "insert into memotable(name, comment) values(\'"
+ strName + "\',\'" + strComment + "\')";
PreparedStatement ps = refConnection.prepareStatement( strSQL );
ps.executeUpdate();
}
// データベースからデータを抽出
{
ArrayList refList = new ArrayList();
if (refConnection != null)
{
String strSQL = "select * from memotable order by time desc";
PreparedStatement ps = refConnection.prepareStatement( strSQL );
ResultSet rs = ps.executeQuery();
while (rs.next())
{
MemoTable refMemoTable = new MemoTable();
{
refMemoTable.setName( rs.getString("name") );
refMemoTable.setComment( rs.getString("comment") );
refMemoTable.setTime( rs.getString("time") );
}
refList.add( refMemoTable );
}
}
req.setAttribute( "MemoList", refList);
}
return mapping.findForward( "success" );
}
}
PerparedStatement の実行にはexcuteUpdate() とexcuteQuery() の二つがあり、値を返す場合は後者です。戻り値のResultSet にはレコードが
保持されていて、最初は先頭のひとつ前を指しています。ResultSet::next() を繰り返してシーケンシャルにアクセスします。memotable クラスに
値を格納して行きます。
後はコンパイルしてTomcat をリスタートすれば完了です。
動作確認
ブラウザで/bbs05/bbs.jsp にアクセスして確認してください。
エラーについて
発生しがちなエラーについてです。
Cannot load JDBC driver class 'com.mysql.jdbc.Driver'
ダウンロードしたMySQL Connecter/J の中にmysql-connector-java-3.1.10-bin.jar 以外の他のjar(例えばmysql-connector-java-3.1.10-bin-g.jar) などが
ありますが、これを両方$CATALINA_HOME/common/lib に入れるとこのエラーが出ます。
日本語が??? になる
データベースの文字セットがutf8 でないと出るようです。
Cannot get a connection, pool exhausted
アクセスが集中している訳でも無く、しかもある日突然エラーとなって回復しないトラブルです。単純にコネクションリソースの枯渇を意味し、
明示的にコネクションリソースを閉じないコーディングが原因です。参考文献
struts [validator]のサンプル(bbs02.war) をベースに、実装してゆきます。bbs05 として使います。今回のサンプル(bbs05.war)