連載
» 2005年09月03日 00時00分 公開

Seaser Projectの全貌を探る(3):SeasarV2によるDBアクセス機能 (2/3)

[沖林正紀,@IT]

データベースアクセスの実際

 接続先のデータベースとトランザクション制御の方法が設定できましたので、いよいよデータベースに接続してみます。

JDBC APIを利用して接続する例

 ここでは、アプリケーションの内部で直接JDBC APIを利用し、オブジェクトへのマッピングも同時に行う例を紹介します。

 サンプルはシンプルな小遣い帳です。表2に示すとおり、accountdbというデータベースに、実際に使った金額などが記録されるaccountテーブルと、カテゴリを登録しておくcategoryテーブルとを用意しておき、それらにデータを登録したり、またはデータを読み出したりしてみます。

categoryテーブル
フィールド データ型 制約
id INTEGER PRIMARY KEY
name VARCHAR(100) UNIQUE

accountテーブル
フィールド データ型 制約
id INTEGER PRIMARY KEY
id_cat INTEGER FOREIGN KEY REFERENCEScategory(id)
title VARCHAR(200) NOT NULL
amount INTEGER NOT NULL
memo TEXT -
date_create DATE NOT NULL
date_modified DATE NOT NULL
表2 accountdbに作成したテーブル

 まずデータベースへのアクセスを担うDAO(Data Access Object)クラスとインターフェイスを作成します。メソッドはカテゴリごとの金額などを問い合わせるgetAccountByCategoryメソッド、accountテーブルにデータを追加するinsertAccountメソッド、同テーブルのデータを更新するmodifyAccountメソッドとします。実際に作成したインターフェイスをリスト1に示します。また、accountテーブルの1行文のデータを保持するクラスをリスト2に示します。

リスト1 データベースにアクセスするDAO(インターフェイス) [AccountDao.java]
package myfirst.jdbc;

import java.util.List;
import java.sql.SQLException;

public interface AccountDao {
  List<Account> getAccountByCategory(int id) throws SQLException;
  int insertAccount(Account account) throws SQLException;
  int modifyAccount(Account account) throws SQLException;
}

リスト2 accountテーブルのデータを表すクラス[Account.java]
package myfirst.jdbc;

import java.util.Date;

public class Account  {

  private int id;
  private Category category;
  private String title;
  private int amount;
  private String memo;
  private Date createDate;
  private Date modifiedDate;

  public void setId(int id){this.id = id;}
  public void setCategory(Category category) {this.category  = category;}
  public void setTitle(String title) {this.title  = title;}
  public void setAmount(int amount) {this.amount = amount;}
  public void setMemo(String memo) {this.memo = memo;}
  public void setCreateDate(Date cDate) {this.createDate = cDate;}
  public void setModifiedDate(Date mDate) {this.modifiedDate = mDate;}

  public int getId() {return id;}
  public Category getCategory() {return category;}
  public String getTitle() {return title;}
  public int getAmount() {return amount;}
  public String getMemo() {return memo;}
  public Date  getCreateDate() {return createDate;}
  public Date  getModifiedDate() {return modifiedDate;}

  public String toString() {
 return
 String.format( "Account [ id = %d, category = %s, title = %s, amount = %d, memo = %s, createDate = %s, modifiedDate = %s ]", id, category, title, amount, memo, createDate, modifiedDate );
  }

}

 ではgetAccountByCategoryメソッドをどのように実装するのかを見ていきましょう。まずデータベースにアクセスするためのSQLを設定します。ここでは問い合わせ処理ですからSELECT文です。ここではPreparedStatementを利用するため、パラメータの部分を“?”としておきます。SELECT文の途中でAS句を使用しているのは、データ取得時に違うテーブルの同じフィールド名を区別できるようにするためです。

String selectSql = "SELECT a.id AS a_id, c.id AS c_id, c.name AS c_name, a.title, a.amount, a.memo, a.date_create, a.date_modified FROM account a, category c WHERE a.id_cat = c.id AND a.id_cat = ? ORDER BY a.id";

 その後、これを実行してResultSetを取得するのですが、Connectionオブジェクトは実装クラス(AccountDaoImpl.java)のコンストラクタより注入されたDataSourceから取得します。

Connection conn = ds.getConnection(); // データベース接続(DataSourceから取得)
PreparedStatement ps = conn.prepareStatement( selectSql ); 

ps.setInt( 1, id ); // カテゴリを表すIDを設定
ResultSet rs = ps.executeQuery(); // SQL(SELECT文)を実行

 データベースからデータを取得したら、あとは1行分ずつAccountオブジェクトを生成して、各フィールドのデータをこのオブジェクトのプロパティに設定します。最後はこれをまとめたArrayListオブジェクトを戻り値とすれば、メソッドが完成します。

ArrayList<Account> al = new ArrayList<Account>();  // 検索されたデータを保持
while( rs.next() )  {
  Category c = new Category();
  c.setId  ( rs.getInt   ( "c_id"   ) );
  c.setName( rs.getString( "c_name" ) );
  Account a = new Account();
  a.setId          ( rs.getInt   ( "a_id"          ) );   // ID
  a.setCategory    ( c                               );   // カテゴリ
  a.setTitle       ( rs.getString( "title"         ) );   // 用途
  a.setAmount      ( rs.getInt   ( "amount"        ) );   // 金額
  a.setMemo        ( rs.getString( "memo"          ) );   // メモ
  a.setCreateDate  ( rs.getDate  ( "date_create"   ) );   // データ作成日
  a.setModifiedDate( rs.getDate  ( "date_modified" ) );   // データ更新日
  al.add( a );  // データ追加
}

 このメソッドの実行例を以下に示します。この中でDEBUG〜とあるのは、S2から自動的に出力されたログです。カレントディレクトリに、S2で用意されているlog4j.propertiesファイルをコピーしておくと、実行時にログが表示されます。このファイルを書き換えるとログの出力先を変更することができます。

   出力されたログ(getAccountByCategoryメソッドの実行例(id = 1のデータを取得)
DEBUG 2005-07-01 16:12:08,208 [main] トランザクションを開始しました
DEBUG 2005-07-01 16:12:08,739 [main] 物理的なコネクションを取得しました
DEBUG 2005-07-01 16:12:08,749 [main] 論理的なコネクションを取得しました
DEBUG 2005-07-01 16:12:08,809 [main] トランザクションをコミットしました
Account [ id = 4, category = Category [ id = 1, category = 食費 ], title = スーパーで買い物, amount = -4500, memo = ちょっと買い過ぎたかな, createDate = 2005-06-24, modifiedDate = 2005-06-24 ]
DEBUG 2005-07-01 16:12:08,839 [main] トランザクションを開始しました
DEBUG 2005-07-01 16:12:08,839 [main] 論理的なコネクションを取得しました
DEBUG 2005-07-01 16:12:08,929 [main] トランザクションをコミットしました

 今度は、INSERTを行うinsertAccountメソッドを実装してみます。SQL文は以下のように設定しておきます。

String insertSql = "INSERT INTO account ( id_cat, title, amount, memo, date_create, date_modified ) VALUE ( ?, ?, ?, ?, CURDATE(), CURDATE() )";

 PreparedStatementオブジェクトを用意するところまでは先ほどのメソッドと同じです。その後、各パラメータにAccountオブジェクトのプロパティの値を設定し、SQL文を実行します。これでメソッドの実装ができました。UPDATEを行うメソッドも、これとほぼ同じ要領で実装することができます。

ps.setInt ( 1, account.getCategory().getId() ); // カテゴリ(食費)
ps.setString( 2, account.getTitle() ); // 用途
ps.setInt ( 3, account.getAmount() ); // 金額
ps.setString( 4, account.getMemo() ); // メモ
int rows = ps.executeUpdate(); // SQL(SELECT文)を実行

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。