連載
» 2005年12月23日 00時00分 公開

Seaser Projectの全貌を探る(5):SeasarのO/RマッピングツールS2Dao (1/2)

Seasar(シーサー)は、国内のコミュニティ「The Seasar Project」によって開発が行われているオープンソースプロダクトだ。DI+AOPコンテナとして評価が高いSeasarV2は、J2EE開発の現場にも影響力を持ち始めた。例えば電通国際情報サービスがSeasar Projectを正式に支援することを表明し、2005年6月からは同社による商用サポートサービスが開始されている。本連載では、同プロジェクトの代表的なプロダクトを紹介していく。(編集局)

[沖林正紀,@IT]

 第2回「DI+AOPを実現するSeasarV2」と第3回「SeasarV2によるDBアクセス機能」ではSeasarV2(S2)本体の機能であるDI(依存性の注入)とAOP(アスペクト指向プログラミング)について紹介してきました。今回からは、S2本体の機能を使ったS2プロダクトを紹介していきます。

 始めはS2本体と同じ開発者の手によるO/RマッピングツールであるS2Dao(http://www.seasar.org/s2dao.html)を紹介します。このツールにより、S2のAOP機能を用いたDBアクセスが可能になり、オブジェクトの永続化を実現することができます。

 なお、本稿で使用しているのはS2バージョン2.2.10とS2Daoバージョン1.0.28です。S2のバージョンに対してS2Daoのどのバージョンが対応するかを示した表が「S2主要プロダクト リリースタイミング一覧」(http://www.seasar.org/versions.html)にありますので、それぞれのバージョンをこの表で確かめてから実行環境を構築してください。

S2Daoの特長

 まずS2Daoというプロダクトの特長を紹介しましょう。

  • XMLファイルを使わない

 HibernateなどのO/Rマッピングツールでは、オブジェクトのプロパティとRDBテーブルの各カラムのデータとを対応付ける(マッピングする)ために、XMLファイルによる設定を記述しなければなりません。しかしS2Daoでは、こうしたマッピングの設定をstatic final型のクラス変数を用いて行います。このような設定のための変数をS2Daoではアノテーションと呼んでいます。アノテーションといっても、メタデータを記述するという点では同じですが、J2SE5のそれとはまったく内容が異なりますので間違えないようにしてください。具体的にどのようなものなのかは後述します。

  • JDBC APIを使わない

 S2DaoはO/Rマッピングツールです。このジャンルのツールでは、オブジェクトのやり取りだけでDBアクセスができるというのが大きな特徴です。それはS2Daoも同じです。ですからデータベースを接続・切断するためのソースコードを記述する必要がありません。また、S2本体が持つトランザクション機能と組み合わせることもできます。この機能もAOP機能を利用しているわけですから、diconファイルに設定を記述するだけでO/Rマッピング機能をアプリケーションにプラスすることができるわけです。

  • SQLとJavaのソースコードを分離する

 S2Daoの機能として、決められた形式に従った名称を持つファイルにSQLソースコードを記述しておくと、その内容(SQL)をデータベースに対して実行してくれるという機能があります。これにより、SQLソースコードを記述するDB技術者とJavaソースコードを記述するJava技術者の作業を分離することができます。

  • SQL文の自動生成

 ある命名規則に従ってJavaメソッドを作成すると、それを基にしてS2DaoがSQL文を自動的に生成してくれます。実行したいSQL文がシンプルなもので、上記のように別ファイルでSQL文を書くまでもないというような場合は、その作業すら省略することができます。

  • インターフェイスだけで処理を実行

 上記のようなSQLのJavaソースコードからの分離やSQL文の自動生成機能のおかげで、diconファイルにインターフェイスの設定を記述するだけで必要な処理が実行できるようになります。もちろんより複雑な処理を行う場合には、diconファイルにそれを実装したクラスについての設定を記述することもできます。

  • EntityManagerによる検索処理の実行

 EntityManagerというのは、EJB 3.0のそれとは異なり、S2Daoで利用できるオブジェクトのことをいい、検索処理の記述を簡単にするメソッドを多数用意しています。検索処理が多いアプリケーションを開発する場合は、こうした機能を活用すれば開発効率の向上が期待できるでしょう。

  • バージョン番号やTimestampによる排他制御

 バージョン番号を持つint型のプロパティやjava.sql.Timestamp型のプロパティをエンティティクラス(後述)に作成しておくと、データの更新時に、更新しようとするデータ(エンティティ)のバージョン番号やTimestampの値と、データベースに保存されているデータのバージョン番号やTimestampの値とが同じものでないと更新することができないようにすることができます。

  • S2DaoTestCase

 S2を用いたアプリケーションのテストを行うS2TestCase(第4回「SeasarV2によるテスト機能」参照)を継承したもので、assertBeanEquals()メソッドなどを実装しています。このクラスを用いることにより、Excelファイルを使ったDBアクセス処理のテストを行うことができるようになります。詳細はSeasarプロジェクトのドキュメント(http://www.seasar.org/testtech.html#S2DaoTestCase)を参照してください。

インターフェイスだけでO/Rマッピング

 では、どのようにしてS2DaoでO/Rマッピングを行うのかを紹介していきます。使用するデータベースは前回「SeasarV2によるテスト機能」と同じaccountdbです。ここではまずcategoryテーブルに対してアクセスするための処理を行うDAOインターフェイスをリスト1のように作成しました。そしてcategoryテーブルとデータのやり取りを行うエンティティクラスとしてリスト2を作成しました。これだけでO/Rマッピングが可能なのかを実際に試してみましょう。

リスト1 categoryテーブルにアクセスするためのDAOインターフェイス(CategoryDao.java)
package myfirst.dao;

public interface CategoryDao  {

  // アノテーションによるマッピングの設定
  public static final Class BEAN = Category.class;

  // DBアクセス用のメソッド(自動生成用)
  List<Category> getCategories();                  // SELECT用
  public Category getCategory( int id );
  public int insertCategory( Category category );  // INSERT用
  public int modifyCategory( Category category );  // UPDATE用
  public int deleteCategory( Category category );  // DELETE用


リスト2 categoryテーブルとデータのやりとりをするエンティティクラス (Category.java)
package myfirst.dao;

public class Category  {

  // アクセスするテーブル名称を示すアノテーション
  public static final String TABLE = "CATEGORY";

  private int    id;      // IDカラムの値
  private String name;    // NAMEカラムの値

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

  public int    getId  ()             {  return id;    }
  public String getName()             {  return name;  }

  public String toString()  {
    return
      String.format( "Category [ id = %d, category = %s ]", id, name );
  }

  public int hashCode() {  return getId();  }


 リスト1リスト2のそれぞれにstatic final型のクラス変数が定義されています。これがS2Daoのアノテーションです。リスト1のクラス変数は、どのエンティティクラスとデータをやりとりすればよいかを表すBEANアノテーションです。そしてリスト2の変数は、どのテーブルのカラムと対応付けられるのかを表すTABLEアノテーションです。この2つの設定により、CategoryDaoはデータベースのcategoryテーブルとマッピングされたことになります。

 前回と同様のデータがあらかじめ保存されているとして、ここに新しく“通信費”というカテゴリを追加する処理を行うJavaソースコード(リスト3)を記述し、実行してみましょう。リスト4のようにdiconファイルを作成しておくことも忘れないでください。ちなみに、リスト4でインクルードしている“j2ee.dicon”は筆者の実行環境に合わせてデータベース設定などを書き換えたものを用いています。

 そうするとリスト5のように実行結果が出力されます。リスト5の上から5行目が自動生成されたSQL文です。idカラムはデータベース側で自動生成されるようにテーブルが作成されているため、ここでは値が設定されませんが、ひとまずリスト6のようにデータが追加されたことが確認できます。

リスト3 CategoryDaoインターフェイスにより新しいカテゴリを追加する(抜粋)
// コンテナの設定(diconファイルの読み込み)
S2Container container = S2ContainerFactory.create( "CategoryDao.dicon" );
container.init();
// DAOオブジェクトの取得
CategoryDao dao = (CategoryDao)container.getComponent( CategoryDao.class );
// 追加するカテゴリ
Category c = new Category();
c.setName( "通信費" );
// categoryテーブルに新しいカテゴリを追加する
dao.insertCategory( c );
// コンテナの終了処理
container.destroy();
  

リスト4 diconファイルの内容(CategoryDao.dicon)
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
  <include path="mydao.dicon" />
  <component class="myfirst.dao.CategoryDao">
    <aspect>dao.interceptor</aspect>
  </component>
</components>
mydao.diconの内容
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
                            "http://www.seasar.org/dtd/components.dtd">
<components namespace="dao">
  <!-- DB関連の設定 -->
  <include path="j2ee.dicon" />
  <!-- S2DaoInterceptorコンストラクタ引数 -->
  <component class="org.seasar.dao.impl.DaoMetaDataFactoryImpl" />
  <component name="interceptor"
             class="org.seasar.dao.interceptors.S2DaoInterceptor" />
</components>

   リスト5 リスト3の実行結果(例)

DEBUG 2005-08-26 15:57:23,691 [main] 物理的なコネクションを取得しました
DEBUG 2005-08-26 15:57:23,691 [main] 論理的なコネクションを取得しました
DEBUG 2005-08-26 15:57:23,821 [main] 論理的なコネクションを閉じました
DEBUG 2005-08-26 15:57:23,901 [main] 論理的なコネクションを取得しました
DEBUG 2005-08-26 15:57:23,911 [main] INSERT INTO CATEGORY (id, name) VALUES (0, '通信費')
DEBUG 2005-08-26 15:57:23,991 [main] 論理的なコネクションを閉じました
DEBUG 2005-08-26 15:57:23,991 [main] 物理的なコネクションを閉じました


   リスト6 “通信費”というカテゴリが追加された

mysql> select * from category order by id;
+----+--------+
| id | name |
+----+--------+
| 1 | 食費 |
| 2 | 交通費 |
| 3 | 書籍代 |
| 4 | 光熱費 |
| 5 | 家賃 |
| 6 | 給料 |
| 7 | 通信費 |
+----+--------+
7 rows in set (0.00 sec)


SQL自動生成のためのメソッドの命名規則

「S2Daoの特徴」でも紹介したとおり、決められた命名規則に従って作成されたメソッドをインターフェイスに記述しておくと、それを基にS2DaoがSQL文を自動生成します。その命名規則とは、以下のようなものです。

更新処理
INSERT文 …… メソッド名をinsert、add、createで始める
UPDATE文 …… メソッド名をupdate、modify、storeで始める
DELETE文 …… メソッド名をdelete、removeで始める
引数 …… エンティティクラス
戻り値 …… voidかint

検索処理
SELECT文 …… 戻り値の型をエンティティクラスもしくはその配列、あるいはListにする
(これ以外の型の場合は実行結果の1行につきカラムが1つと見なされる)


DAOインターフェイスに記述できるアノテーション

インターフェイスやその実装クラスには、BEAN以外にも以下のアノテーションを記述することができます。これらはすべてstatic final String型のクラス変数とし、変数名は“メソッド名_アノテーション名”とします。

詳細はS2Daoのドキュメント(http://www.seasar.org/s2dao.html)を参照してください。

ARGSアノテーション
SQLコメントに記述する引数とメソッドの引数とを関連付ける
(変数が複数のときはカンマ区切り、使用例は後述)

QUERYアノテーション
自動生成されるSELECT文の後に追加するWHERE句やORDER BY句
(WHEREは省略するが、最初にORDER BY句を記述するときは"ORDER BY"を設定する)

PERSISTENT_PROPSアノテーション
永続化したい(SQLに含めたい)プロパティの名称を設定する
(プロパティが複数のときはカンマ区切り)

NO_PERSISTENT_PROPSアノテーション
永続化したくない(SQLに含めたくない)プロパティの名称を設定する
(プロパティが複数のときはカンマ区切り)

SQLアノテーション
実行したいSQL文を直接ここに設定する


エンティティクラスに記述できるアノテーション

エンティティクラスには、TABLE以外にも以下のアノテーションを記述することができます。
詳細はS2Daoのドキュメント(http://www.seasar.org/s2dao.html)を参照してください。

COLUMNアノテーション [変数名は"プロパティ名_COLUMN"]
プロパティ名とカラム名が異なるときに、それらを関連付け。第3回では、AS句を使ってカラム名とプロパティ名を合わせていたが、このアノテーションを使えばそれをしなくて済む

IDアノテーション [変数名は"プロパティ名_ID"]
プライマリキーのIDを設定するプロパティの名称

NO_PERSISTENT_PROPSアノテーション
永続化したくない(SQLに含めたくない)プロパティの名称
(プロパティが複数のときはカンマ区切り)

VERSION_NO_PROPERTYアノテーション
バージョン番号による排他制御を行うときに用いるプロパティの名称

TIMESTAMP_PROPERTYアノテーション
Timestampによる排他制御を行うときに用いるプロパティの名称


       1|2 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

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

メールマガジン登録

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