連載
» 2006年01月19日 00時00分 公開

Seaser Projectの全貌を探る(6):SeasarのDBアクセスにHibernateを使う (3/3)

[沖林正紀,@IT]
前のページへ 1|2|3       

S2Hibernate.daoによるDBアクセス

 今度は、HibernateのSessionオブジェクトを操作する代わりに、S2ではおなじみのアノテーションを用いた設定を行ってみましょう。S2のアノテーションについては何度も説明していますが、これはS2独自のstatic final型の変数を用いた設定の仕組みのことをいい、JDK5.0のそれとは別物です。

 S2Hibernateで用意されているアノテーションは、以下に示すBEAN・HQL・ARGS・PROPERTYの4種類があります。

  • BEANアノテーション

 DAOと関連付けられるDTOクラスを設定します

(例)
  public static final Class BEAN = Account.class;

  • HQLアノテーション

 “(メソッド名)_HQL”という変数名を持つString型の変数にHibernateで実行するHQL文を設定します。

(例)同じカテゴリーの小遣い帳の明細(Accountオブジェクト)を取得する
public static final String getAccountByCategory_ARGS =
 "category";
public static final String getAccountByCategory_HQL  =
 "from Account a where a.category = :category";
List getAccountByCategory( Category category );

  • ARGSアノテーション

 DAOインターフェイスのメソッドに設定されている引数の名称を設定します。このとき“maxResults”と“firstResult”という名称が、それぞれデータを取得する件数とデータ取得の開始行を表す予約語となっているため、それらの値をメソッドの引数とする場合には、引数の名称を必ずこれに合わせる必要があります。

 また、このアノテーションに設定された引数名がDTOのプロパティ名と同じである場合は、自動的にこれらがwhere句に追加されます。

(例1)ID番号をもとにaccountテーブルのデータをAccountオブジェクトとして取得する
public static final String getAccount_ARGS = "id";
// 上記のARGSアノテーションだけで、以下のHQL文を設定するのと同じ
// public static final String getAccount_HQL
  = "from Account a where a.id = :id";
Account getAccount ( int id );

 また、引数名に不等号をつけると、where句が不等号をつけた状態で生成されます。

(例2)amountの値が、ある数より大きいものだけを取得する(0以上にすれば収入の分だけ取得できる)
public static final String getIncomeAccounts_ARGS = "amount >";
List getIncomeAccounts( int amount );

  • PROPERTYアノテーション

 テーブルのカラム名とDTOのプロパティ名が異なる場合にARGS用います。HQL文である値の範囲を制限する場合などに適しています。

(例)金額がfromExpenseからtoExpenseまでの範囲にあるaccountテーブルのデータを取得する
public static final String getExpenseAccounts_PROPERTY = "amount >
 fromExpense, amount < toExpense";
List<Account> getExpenseAccounts( ExpenseAccount ea );
// ※ ExpenseAccountはfromExpense, toExpenseをプロパティに持つPOJOとする

SELECT編

 では、これらのアノテーションにより、どのようにしてDBアクセスが行われるのかを紹介しましょう。まずは金額ある範囲にある支出についてaccountテーブルから取得してみましょう。

 まずDAOインターフェイスをリスト11のように作成します。ここではHQL・ARGSの各アノテーションを用いています。これによりamountプロパティの値がfromExpenseからtoExpenseの範囲にあるAccountオブジェクトを取得することができます。そしてこの実装クラスは作成せずに、リスト12のようにして実行します。結果はリスト13のようになります。この間Sessionオブジェクトには全くふれずにデータを取得する処理を行うことができました。

 ちなみに、この処理を実行するにあたってdiconファイルをリスト14のように作成しました。アスペクトの設定としてs2hibernate3.interceptorが追加されています。これはs2hibernate3.diconに設定されているもので、アノテーションの解釈とDBアクセスを可能に必要なものです。

リスト11 DAOインターフェイス(AccountDao.java)
package myfirst.hibernate;

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

public interface AccountDao  {

  public static final Class BEAN = Account.class;

  // amountの値がfromExpenseからtoExpenseの範囲にある支出を取得する
  public static final String getBetweenAccounts_ARGS 
        = "fromExpense,toExpense";
  public static final String getBetweenAccounts_HQL 
        = "from Account a where a.amount between
              :fromExpense and :toExpense";
  List getBetweenAccounts( int fromExpense, int toExpense );

  // ..... 略 ......
}

リスト12 DBアクセスによりAccountオブジェクトを取得
S2Container container = S2ContainerFactory.create( "diconファイルのパス" );
container.init();
try {
  AccountDao dao 
    = (AccountDao)container.getComponent( AccountDao.class );
  for( Object a : dao.getBetweenAccounts( -10000, -1000 ) ) 
       System.out.println( (Account)a );
} finally {
  container.destroy();
}

  リスト13 リスト12の実行結果(抜粋)

Account [ id = 4, category = Category [ id = 1, category = 食費 ], title = スーパーで買い物, amount = -4500, memo = ちょっと買いすぎたかな, createDate = 2005-06-24 00:00:00.0, modifiedDate = 2005-06-24 00:00:00.0 ]
Account [ id = 6, category = Category [ id = 3, category = 書籍代 ], title = Javaの本いろいろ, amount = -7500, memo = Seasarの本も出ないかな, createDate = 2005-06-25 00:00:00.0, modifiedDate = 2005-06-25 00:00:00.0 ]


リスト14 diconファイルの内容(account.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="s2hibernate3.dicon" />
  <component class="myfirst.hibernate.AccountDao">
    <aspect>j2ee.requiredTx</aspect>
    <aspect>s2hibernate3.interceptor</aspect><!-- S2Hibernate.dao用 -->
  </component>
</components>

INSERT、UPDATE、DELETE編

 SELECT以外のINSERT、UPDATE、DELETEについては、アノテーションすら設定せずに処理を実行することができます。ではそれぞれの処理をどのようにして行うかというと、メソッドの名称によってどんな処理を行えばよいのかを決定します。メソッドの引数はDTO、戻り値はvoidであることが条件です。

 注意すべきとこととしては、これに関するドキュメントでの記述と実際の処理とが一部で食い違っているところがあるということです。筆者が確かめたところでは、UPDATEを行うメソッドは「update,merge」で始まる名称を持つメソッドに限定されるという点です。これについてドキュメントでは「update,modify,store」で始まる名称を持つメソッドはUPDATE処理が自動的に行われる旨が記述されています。これについては改善を望みたいところです。また、INSERTを行うメソッドはinsertで始まる名称もその対象になります。詳細は、付属ドキュメントの「メソッドの定義」を参照してください。

 DAOインターフェイスとINSERT処理の例をリスト15リスト16にそれぞれ示します。アノテーションが無かったら、一見してDBアクセスをしているように感じないところですが、これもS2HibernateとHibernateによるO/Rマッピングのおかげといえるでしょう。

リスト15 DAOインターフェイス(AccountDao.java)
package myfirst.hibernate;

import java.util.List;

public interface AccountDao  {

  public static final Class BEAN = Account.class;

  void insertAccount( Account account );
  void mergeAccount ( Account account );
  void removeAccount( Account account );
  // ..... 略 ......
}


  リスト16 INSERT処理の例

Category c = new Category();
c.setId( 1 );  // 食費
Account a = new Account();
a.setCategory( c );
a.setTitle( "コンビニで買い物" );
a.setAmount( -500 );
a.setMemo( "最近はいろんなものが置いてあるなぁ" );
a.setCreateDate( new java.util.Date() );
a.setModifiedDate( new java.util.Date() );
dao.insertAccount( a );

 今回はS2でHibernateを利用できるようにするS2Hibernateを紹介しました。Hibernateをバーション2から同3に移行する場合には、再ビルドやAPIの変更などの注意が必要であるものの、新規に使い始める場合には、開発者がSessionオブジェクトにふれなくてもよいなどの利点があります。Hibernateのスキルがある方はぜひ試してみてください。

 次回からはWeb層に関連するプロダクトを紹介します。はじめは、非常に良く知られているフレームワークStrutsを利用できるようにするS2Strutsをご紹介します。

前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

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

メールマガジン登録

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