1.はじめに
これは、springを使ったwebアプリの開発法を例題で学習するための解説です。
次の例題からなります。
(1)例題1 クラスの関連をspringを使って解決する簡単な例(バッチのプログラム)
(2)例題2 簡単なwebアプリをspringを使って書き換えます。
(3)例題3 ActionクラスにServiceの参照を設定するのもspringがするようにします。
(4)例題4 トランザクションの処理をspringに任せます。
(5)例題5 DBMSのアクセスをspringが提供するライブラリ(JdbcTemplate)を使うようにします。
なお、例題2と3、4,5の母体は、「解説:MVC+3層 webアプリの作り方」で使った例題の中の例題3です。「解説:MVC+3層 webアプリの作り方」で使った例題をこの解説の中で参照する場合は「webapp例題n」と呼びます(n=1~3)。この解説の中の例題は単に例題nか、又はspring例題n(n=1~5)と呼びます。
「解説:MVC+3層 webアプリの作り方」で使った例題は次からダウンロードできます。
TODO webapp例題ダウンロード
2.spring例題1
2.1 クラス関連図
例題1のクラス図を図2-1に示します。
図2-1 例題1のクラス図
Starterにmainメソッドが含まれています。
Starterは、まずspringを起動します。springはFirst、Second、Lastを具現化し、各クラスで使う参照を設定します。つまり、mainはspringに「仕事」を依頼するだけです。
springはこの「仕事」をするために「仕事」を定義した情報が必要です。springはこの情報を与える方法を複数個備えています。この例題ではすべてxml形式で外部ファイルに保存された情報を使います。詳細は後の節で説明します。
springの「仕事」の後、StarterはFirstクラスのcalcAmountを呼び出します。その後の処理は次の節で説明します。
2.2 処理の概要
図2.2-1に例題のシーケンス図を示します。
図2.2-1 例題1のシーケンス図
FirstのcalcAmountからSecondのgetListメソッドを呼び出し、SecondがgetStringListを呼び出します。
LastのgetStringListが戻すデータは、複数組の/で区切られた数字列です。
データの例を次に示します。
100/10/3
/25/7
38
57/33/28
SecondのgetListは先頭の数字のリストを戻します。先頭が/の場合は、最初の数字は0とみなします。先ほどの例の場合、次のデータを戻します。
100, 0, 38,57
FirstのcalcAmountは、Secondから戻された数のリストの合計を計算して、合計値を呼び出し元に戻します。先の例の場合は次の値を戻します。
195
2.3 Springのパラメータ
springに仕事を指示するパラメータをリスト2.3-1に示します。このリストを含んだファイルをspringコンテキストファイルと呼びます。この内容(情報)もしくはこの内容を保持しているオブジェクトをspringコンテキストと呼びます。この解説の例題では、springコンテキストをxmlファイルで与えます。
リスト2.3-1 springのコンテキストxml
|
注:各行の先頭の数字は説明のための番号です。実際のデータには付けません。
説明
1 xmlの宣言です。
2 doctype文です。このまま使います。
3 springのコンテキストxmlのルート要素はbeansです。
4~5 Lastクラスのインスタンスを定義します。idはコンテキストデータの中でこのインスタンス(bean)を参照するための名称(id)です。これはユニークでなければなりません。classは完全クラス名を指定します。
6~8 Secondクラスのインスタンスを定義します。idとclassについてはLastの場合と同じです。Lastと違ってproperty要素が定義されています。nameはプロパティの名称です。refはこのプロパティに設定する値です。特にrefはコンテキストデータの中の他のインスタンス(bean)の参照を設定することを意味します。該当するインスタンス(bean)のidをここに指定します。この場合はtheLastですから、4~5で定義されたLastが該当します。
なお、プロパティとしては、基本の型の値やマップ(Map)、リスト(List)、Properties(Mapの特別な場合)を設定できます。その詳細は例題の範囲外ですので説明は省きます。
9~11 Firstクラスのインスタンスを定義します。Secondクラスと同じです。
12 閉じのタグです。
(説明終り)
springのコンテキストxmlで参照がインスタンスのプロパティとして設定されることを説明しました。例として、Second クラスを見てみます。
リスト2.3-2 Second.java
|
プロパティの名称は、Java beanの約束に従っています。たとえば、プロパティ名がlastの場合、それを設定するメソッド名はsetLastです。つまり、プロパティ名の先頭文字を大文字にして、頭にsetをつけたものが設定するメソッド名になります。
これが、3~5で定義されているメソッドです。ここではフィールドlastに引数で渡された値を設定しています。フィールドの名称はプロパティ名とは関係ありません。しかし、便宜の点から通常は同じ名前が使われます。
プロパティの値を取り出すメソッド名にはsetの代わりにgetが付きます。この例では不要ですので定義していません。
2.4 springの起動
この例題ではClassPathXmlApplicationContextを使ってspringを起動しています。他にも方法がありますがここでは触れません。
Starterの該当部分をリスト2.4-1に示します。
リスト2.4-1
|
説明
3 ClassPathXmlApplicationContextの引数
第1引数:コンテキストxmlのファイル名
第2引数:コンテキストxmlのファイルが存在するパッケージ内のクラス。ファイルのパスを決めるために使われるだけで内容はコンテキストxmlの内容に関係ありません。
4 IFirst first = (IFirst)ctx.getBean("theFirst");
springが具現化したインスタンス(bean)の参照を得るための処理です。この場合はidがtheFirstのインスタンスの参照を得ます。
この文ではspringが具現化したインスタンスをidで参照しています。これは本来のDIの原則に反するものです。ただ、mainメソッド又は類似の役割のメソッドに限定すれば、この方法を使わざるを得ない場合があります。こういう場合に限って使うべきです。ここでは、Starterの再利用性を考慮しなくてもよいのでこの方式を使っています。
5 int amount = first.calcAmount();
FirstクラスのcalcAmountメソッドを呼び出しています。
(説明終り)
2.5 各クラスの処理
全体の処理については「2.2 処理の概要」で説明しました。
この節では、Starterを除く各クラスのメソッドの処理ががspringを意識しないで作られていることを説明します。この点に関してはどのクラスも同じですので、FirstクラスのcalcAmountを見てみます。リスト2.5-1はこのメソッドのコードです。
リスト2.5-1 First. calcAmount
|
3行目で使っているsecondはspringの仕掛けで設定されます。しかし、このメソッドの中には、secondを設定する処理に関係した内容は何もありません。
2.6 spring例題1のまとめ
この例題で次のことが習得できました。
(1) springの使い方
(2) 起動する処理を除くと、実装したメソッドにspringは影響しない。
3.spring例題2
3.1 spring例題2の概要
spring例題2ではwebアプリにspringを適用します。
母体として、webapp例題3(「解説:MVC+3層 webアプリの作り方」の例題3)を使います。webapp例題3はDIを使ったwebアプリです。ただ、springは使わないで、Initializerクラスでspringもどきの処理をしました。ここをspringで対応するように変更します。
spring例題2の構造を図3.1-1に示します。
図3.1-1 spring例題2の構造
ActionからDataSourceまで層構造をしています。このうちServiceからDataSourceまではspring例題1と同じ仕掛けでクラス間を関連付け出来ます。
しかし、Actionについては、それと同じ方法が使えません。
Actionが他のクラスと違うところは、Actionを具現化するのはstrutsであるということです。そのためActionを具現化する時期や方法が隠蔽されています。だから、プログラムの起動時(=springの起動時)に、springはActionのインスタンスにServiceの参照を設定できないのです。springはこの状況に対応する機能を提供しています。それは次の例題で説明します。この例題ではそれとは別の、図3.1-2に示す方法を使います。
図3.1-1ではデータ(利用者の要求)の処理の流れを核に描いていますので、図3.1-2に現れるRequestとControllerは略されています。これらは、利用者の要求を保持し、その要求を処理するActionに制御を移す役割を担います。Serviceの参照を設定する仕掛けを理解するには、この流れが必要ですので、図3.1-2にはRequestとControllerを含めています。
図3.1-2 ActionへServiceの参照を設定する方法
この方法は次の通りです。
(1) プログラムの起動時
InitializerはServiceの参照をサーブレットコンテキストに保存します。
(2) Action実行時
Actionの本来の処理の前に、サーブレットコンテキストからServiceの参照を取得する処理を入れます。
これは、親(ベース)のクラスを作って、そこで行うと通常のActionクラスでは、DIと同じ効果でserviceを参照できます。
webapp例題3のInitializerクラスで実行していた実質的な処理を取り除き、spring例題2では、springを起動する処理だけにします。propertiesファイルは不要になます。その内容はspringのコンテキストxmlに移します。
webapp例題3からspring例題2を作る具体的な方法は付録で説明します。
3.2 Initializerクラス
spring例題2のInitializerクラスをリスト3.2-1に示します。
リスト3.2-1 Initializerクラス
|
実質的な処理は5~10のtry句の中です。この部分だけ説明します。
(説明)
6 springを起動します。
7~8 Serviceのインスタンス参照を取り出します。spring例題1のStarterの処理と同じです。
9 Serviceのインスタンス参照をサーブレットコンテキストに設定します。そのキーは2行目で設定しています。キーには他のデータのキーと重ならないようにするため、Serviceの完全クラス名を使っています。
(説明終り)
webapp例題3では6行目の処理が、Initializerクラスの中に実装していました。
第7行~第9行の処理は各アクションにServiceの参照を設定するための準備として必要な処理です。springにそのための機能が備わっていますが、この例題ではまだ使いません。
3.3 コンテキストxml
リスト3.3-1 コンテキストxml
|
spring例題1と同じ部分は略します。
(説明)
1~7 データソースの定義。この例題では例題用のデータソースConnectionBuilderを使います。設定するプロパティとして次の4個があります。
driverClassName:DBMSのドライバークラス
url:DBのurl
username:DBのユーザ名
password:DBのパスワード
これらの値の型はすべて文字列です。定まった文字列の値(文字定数)はvalue属性で指定します。
8~10 DbAccessorの定義。プロパティとしてはデータソースを設定します。
11~14 daoの定義。プロパティとして次の二つがあります。
DbAccessorのインスタンス参照
テーブル名称(型は文字列)
15~17 serviceの定義 プロパティとしてdaoを設定します。
(説明終り)
参考までにwebapp例題3のpropertiesをリスト3.3-2に示します。該当する情報はプロパティ名から対応付けられると思いますので詳細は略します。webapp例題3のpropertiesでは、インスタンスの参照を設定するデータはありません。これはInitializerクラスのコードの中に組み込まれています。springのように汎用的なプログラムではこれもプロパティとして定義します。
リスト3.3-2 webapp例題3のproperties
|
4.spring例題3
4.1 spring例題3の概要
spring例題2を改善してspring例題3を作ります。その構成を図4.1-1に示します。
図4.1-1 spring例題3の構成
spring例題2では各アクションにServiceの参照を設定する処理を自前のInitializerクラスとActionクラスでしています。spring例題3ではこの処理をspringが提供するクラスにまかせます。結果として、Initializerクラスが不要になります。また、InitializeクラスのinitProgramを呼び出していた、ベースアクションに記述した処理も不要になります。
さらに、spring例題2ではlistenerを使ってspringを起動しましたが、spring例題3ではstrutsのplug-inで起動します。このplug-inはspringが提供します。
spring例題2からspring例題3を作る具体的な方法は付録で説明します。
4.2 ベースアクションの変更
リスト4.2-1はspring例題2とspring例題3のベースアクションの違いを説明するリストです。
リスト4.2-1 ベースアクション
|
spring例題2から削除された部分
2,5,6
これは、サーブレットコンテキストに保存されているserviceの参照を取り出してフィールドに設定する処理です。
spring例題2に追加された部分
12,13,14
これは、springがserviceの参照を設定するメソッド(setter)です。
まとめると自前のservice設定の処理は不要になって、代わりにspringがserviceを設定するためのメソッドを必要になります。
4.3 plug-inの定義
plug-inの機能はspringに専用の機能ではありません。plug-inはstrutsの機能です。strutsの起動時にするべき処理をするための仕掛けです。struts-configに定義します。
「plug-in」という用語は、「起動時にするべき処理をするための仕掛け」を意味する場合と、その仕掛けで起動されるクラスを意味する場合があります。「plug-in」の仕掛けで起動される「plug-inのクラス」はwebアプリの開発者が目的に応じて自由に開発できます。
通常はこの二つは混乱しないと思いますが、ここでは正確に書くと、strutsのplug-inの仕掛けで起動されるクラスの一つとして、springを初期化するクラスがspringに含まれています。spring例題2で使ったInitializeクラスとそれを起動したリスナーの機能をこれで代替できます。
具体的な指定法をリスト4.3-1に示します。
リスト4.3-1 plug-inの定義
|
(説明)
1 plug-in要素のclassNameは、springのplug-in用のクラスを指定します。
2 set-property要素は一般にplug-inクラスののプロパティを設定するものです。この場合は、springのコンテキストxmlファイルを指定しています。spring例題2のapplicationContext.xmlに相当しますが、この例題では、二つのファイルを指定しています。内容については後の節で説明します。なお、これに対応するspring例題2の情報はInitializeクラスの中に書き込まれていました。
(説明終り)
4.4 アクションのプロクシの設定
各アクションにServiceの参照を設定する処理は、ServiceにDaoを設定するのとは違った工夫が必要です。アクションクラスはstrutsによって具現化され、springで具現化する時期と結果(インスタンス)を管理できない事情があるからです。
こういう場合、springではプロクシを使った方法を使います。springは任意のクラスのプロクシをパラメータの指定で作る機能を備えています。その代表がトランザクションの処理です。これについてはトランザクションの処理で説明します。strutsのアクションのプロクシはトランザクションの場合と違って、strutsのアクションに固有のプロクシです。この節では以後、strutsのアクションのプロクシのことを単にプロクシと呼びます。
プロクシを使わない方法の説明は前の章を参照下さい。
注:プロクシを使わない方法が必ずしも劣っているとは限りません。特にActionのベースクラスをフレームワークが提供するベものでなく、独自に開発して使う場合は、spring例題2で使った、ベースアクションで該当する処理をする方法がむしろ簡潔です。[資料1]を参照下さい。
strutsのアクションのプロクシ(この節では、以後単にプロクシと呼びます)と関連するクラス(群)の関係を図4.4-1に示します。
strutsのcontorollerとwebアプリ開発者が作るアクションクラスの間にプロクシが入ります。
図4.4-1 プロクシを使った場合のアクション周辺のクラス構成
strutsの環境定義(コンフィギュレーション)とspringのコンテキストxmlが協働して、図4.4-1の位置でプロクシが働くようにします。
リスト4.4-1はそのためのstrutsの環境定義です。また、リスト4.4-2は対応するspringのコンテキストxmlの一部です。
リスト4.4-1 strutsのアクション用プロクシを設定するためのstruts設定
|
リスト4.4-2 strutsのアクション用プロクシを設定するためのspring設定
|
次の2点が注目すべき点です。
(1)strutsとspring間で対応を取るために次の値を等しくします。
(a)strutsのaction要素のpath属性
(b)springのbean要素のname属性
リスト4.4-1とリスト4.4-2では/r1_a0203Exampleがこれに相当します。
(2)strutsのaction要素のtype属性には、実際のアクションのクラスではなく、プロクシのクラスを設定します(org.springframework.web.struts.DelegatingActionProxy)。代わりに、実際のアクションのクラスはspringのbean要素のclass属性に設定します。
以上の設定法から分かりますように、webの要求(request)が入ると、strutsのコントローラの働きでspringが提供するプロクシに制御が移ります。プロクシはDIの処理、つまりserviceの参照をアクションに設定する処理をします。
アクションはServiceやDaoと同じbean要素で定義されますので、DIだけでなく他のプロパティの設定も出来ます。
プロクシに関するコンテキストxmlのはこれまで使ったコンテキストxmlに追加して書くことが出来ます。また、一般にコンテキストxmlは分割することも出来ます。この例題では、アクションに関する設定とその他の設定とに分けました。前者はservlet-action.xml、後者はapplicationContext.xmlです(実際の名称は、例題を区分するためにファイル名称の基本部にバージョン番号が付いています)。 これが、4.3-1で指定されているファイルです。
5.spring例題4
5.1 spring例題4の概要
これまでの例題では、トランザクションの処理については触れてきませんでした。実際にはDbAccessorの中にcommitとrollbackを呼び出す処理が入っています。spring例題4ではspringが提供するトランザクションの処理を組み入れます。
springはEJBサーバを代替する狙いを持っており、そのためににはEJBサーバが提供する機能を提供しなければなりません。これまでは、そのうちのDIについて見てきました。これはJNDIに対応する機能です。EJB機能が提供するもう一つの大きな機能はトランザクション処理です。
母体になる例題としてspring例題2を使います。spring例題3を母体として使わない理由は、springのstruts用のplug-inは、strutsを使ったwebアプリ一つに対して一つしか使えないためです(plug-inの仕掛けそのものは複数個のplug-inクラスを起動できます)。
また、例題を簡潔にするために、spring例題2で使ったリスナーは使いません。実際のアプリでは、listener 又はplug-inを使うのがお勧めです。
spring例題4の構成を図5.1-1に示します。
図5.1-1 spring例題4の構成
spring例題2と違う点はトランザクションの処理が入った点とInitializerがActionから使われるところですです。トランザクションの処理については次節で説明します。Initializer がしていた処理はActionがします。その内容は次の通りです。
(1) 起動時
DIに関係した処理はしません。
(2) Action実行時
springがすでに起動(初期化)されたかどうか判定します。これはサーブレットにServiceの参照が設定されているか否かで判断します。
springが起動(初期化)されている場合は、取り出した値をそのまま使います。
springが起動(初期化)されていない場合は、Initializerを呼び出してspringを起動(初期化)します。その後、サーブレットコンテキストにServiceの参照を書き込みます。
この処理は、親(ベース)のクラスを作って、そこで行うと子のActionクラスでは、DIを使ったのと同じ形でServiceを参照できます。
なお、DataSourceとして、DataSource interfaceを製品レベルで実装したDBCP(org.apache.tomcat.dbcp.dbcp.BasicDataSource)を使います。これはtomcatに付属しています。トランザクション処理を「まじめに」行うためには、これまでに使ってきたConnectionBuilderでは十分でないからです。また、ODBCのtext又はEXCELはトランザクションの処理が出来ませんのでMS ACCESSかHSQLDBに切り替えます(もちろんOracleやDB2が使える場合はそれを使っても結構です)。
5.2 トランザクション処理
DBの処理は成功か失敗かのいずれかしか許されません。一部は成功、一部は失敗で完了する処理はありません。処理が成功したときはcommitしてDBへの更新を固定します。失敗のときはrollbackしてそれまでの変更を、変更しない状態に戻します。これをトランザクション処理と言います。
トランザクション処理の例をリスト5.2-1に示します。
リスト5.2-1 トランザクションの処理
|
これを図にすると、図5.2-1になります。丸付きの数字がリスト5.2-1の同じ番号の文に対応しています。
図5.2-1 簡略したトランザクション処理の流れ
この図は主にServiceの周辺の処理を説明するためのものですので、DataSourceの周辺については簡略化しています。そのため正確ではありません。
アクションから要求されたサービス(処理)は次の順序で実行されます。
① トランザクションの開始をTransaction管理に宣言します。
② DBへのアクセスを含む処理をします。
③ commit/rollbackをTransactionクラスに依頼します。
④ トランザクションの終了をTransaction管理に宣言します。
この流れは簡略されていますが、重要なことは、業務ロジックとして意味のある処理は③だけであることです。他の処理は「障害」に対する処理です。図5.2-1の構成の場合、Serviceに「障害」処理が入り込みます。これを避けるために、本来の業務ロジックと「障害」処理を分離した図5.2-2の構成を考えます。
図5.2-2 業務ロジックと「障害」処理の分離
この解説は、springを念頭に置いていますので、分離した一方の処理はproxyとして図5.2-2に記述しています。proxyとTransaction管理は業務ロジックに依存しない部分でspringが提供しています。proxyの部分とTransactionクラスの使い方(パラメータの設定法)を以降の節で説明します。
こうすると、Serviceクラスは業務ロジックだけ実装すれば済みます。これは、業務ロジックを簡潔に実装できるだけでなく、業務ロジックを実装するプログラマはトランザクション処理の知識がなくても済む利点があります。springの狙いの一つである再利用性から得られる利益に疑念を持つ方でも、この後者の利点から得られる利益は直接的ですので、疑いを入れる余地がないと思います。
5.3 コンテキストxml
springで定義するトランザクションに関するコンポーネントの関連を図5.3-1に示します。
図5.3-1 トランザクションに関するコンポーネントの関連
トランザクション処理を含めない場合、serviceはServiceクラスを指していました。トランザクション処理を含める場合はserviceはServiceのプロクシを指します。こうすることでserviceを参照している他のbeanの定義をを変更しなくて済みます。
本来のServiceのidはtargetServiceに変更します。これはプロクシと接続されています。その他の従来からある関連は変わりません。
プロクシはServiceとトランザクション管理(DataSourceTransactionManagerクラス、id=transactionManager)と関連します。
トランザクション管理はdataSourceの関連を持ちます。
これを定義したコンテキストxmlがリスト5.3-1です。
リスト5.3-1 トランザクションに関するコンポネントの定義
|
(説明)
1~9 プロクシの定義。これは「親」に相当します。親子の構造は後で説明します。
このうち、3~7 トランザクションの伝播の扱いとread only属性を設定します。
伝播の扱いについてはこの例題の範囲を超えていますので他の[資料2]を参照下さい。
10~12 プロクシの「子」の定義。 親子で一組のプロクシが定義されます。
13~15 トランザクション管理の定義
16~18 Serviceの定義。これは従来からあるものですがidをserviceからserviceTargetに変更しています。
(説明終り)
この中でプロクシは親子の関係で定義しています。この構造を図5.3-2に示します。
図5.3-2 プロクシの親子関係
この例題では「子」は一つだけです。したがって必ずしも親子関係で定義する必要はありません。ただ、サービスが複数個になる場合は「子」も複数になりますので、それを定義する例として親子関係を定義しています。
5.4 DbAccessorの変更
springが提供するトランザクション処理を使うためには、これまで使ってきたDbAccessorを変更する必要があります。トランザクションに関する処理を取り除き、dataSourceから取得していたコネクションをspringを経由して取得する必要があります。
変更しなければならない具体的な処理は次の通りです。結果はダウンロードしたソースを参照してください。
(1)commit/rollbackの処理を取り除く。
(2)Exception発生時いったんそれをキャッチしてUnsupportedOperationExceptionを投げる。
springのトランザクション処理に頼るには、障害(rollbackする必要がある事象)が起きたときはService(プロクシ)で例外を投げる必要があるためです。
トランザクション処理としては、発生したExceptionをそのままなげてもかまいません。しかし、これらはchecked Exceptionなので、そのまま投げるとDbAccessorのinterfaceを変える必要が出てきます(throws ...を追加しなければなりません)。それで、ここではunchecked ExceptionのUnsupportedOperationExceptionを投げることにします。
(3)setAutoCommit(false)を入れる。(デフォルトはtrueに設定されている)
(4)重要:dataSoutce.getConnection()をDataSourceUtils.getConnection(dataSource)に変える。トランザクション処理をspringに頼るためには、独自で取得したコネクションは使えません。トランザクション管理で管理されたコネクションが必要です。そのためにDataSourceUtils.getConnectionを使わなければなりません。
(5) conn.close()を取る(理由:同上)
5.5 トランザクション処理のテスト用コード
トランザクション処理のテストをするために、わざとエラーになる処理を追加します。追加箇所は次の通りです。
(1) Action
新しいActionとしてExample1ErrorActionを追加します。ここから、Serviceの、エラーが起きるメソッドを呼び出します。
(2) Service
エラーが起きるメソッドを追加します。メソッド名はaddRecordToErrorとします。処理の内容は次の通りです。
2レコード追加する処理をします。最初のレコードの追加処理は正常に終了します。第2の追加はエラーで終わります。その結果、最初の追加はロールバックされて取り消されます。
(3) struts-config
Example1ErrorActionを呼び出すactionを追加します。
詳細は付録とダウンロードしたファイルを参照下さい。
6.spring例題5
6.1 spring例題5の概要
DbAccessorの処理をspringが提供するクラスJdbcTemplateに任せます。この結果DbAccessorは不要になります。この例題ではDbAccessorと同等の機能しか使いませんが、JdbcTemplateは実際のwebアプリで必要になる機能を多く備えています。
また、DbAccessorではコネクションを取るためにspringの提供するクラスをアクセスしていましたが、これはJdbcTemplateの中に入りますので、例題の開発者が直接コネクションを操作しなくて済みます。
spring例題5はspring例題4を母体に開発します。
spring例題5の構成を図6.1-1に示します。DbAccessorに代わってJdbcTemplateが入ります。コンポジションの代わりに親子関係(継承関係)を使います。もちろんコンポジションでも対応できます。その際に使うinterfaceはorg.springframework.jdbc.core.Interface.JdbcOperationsです。
図6.1-1 spring例題5の構成
6.2 検索系の処理
JdbcTemplateの検索系の代表的なメソッドは次の二つです。
(1)queryForObject:(高々)1オブジェクトを戻す。
(2)query:複数のオブジェクトをListで戻す。
具体的なオブジェクトの内容は、マップするクラスのメソッドをオーバライドして与えます。この方法はJdbcTemplateの名前から分かるように設計パターンTemplateの特徴です。
この具体的なコードをリスト6.2-1に示します。匿名のクラスを使うのが一般的です。
リスト6.2-1 マッパーのオーバライド
|
(説明)
2 RowMapper(springが提供するクラス)を継承したクラス(匿名のクラス)を具現化
3 匿名クラスの中にメソッドmapRowを定義します。これは元のクラスRowMapperのmapRowをオーバライドするものです。
(説明終り)
mapRowメソッドの具体的な内容はアプリケーションによって変わります。リスト6.2-1の例は検索結果(ResultSet)をStringクラスの配列に設定しています。こうすることで、ResultSetが開放できる状態に出来ます。
queryメソッドは複数行戻しですので、mapRowが行数の回数だけ呼び出されます。
queryForObjectは高々1オブジェクト戻しですので高々一度だけmapRowが呼び出されます。
6.2 更新系の処理
更新・追加・削除はDbAccessorのインタフェースとほぼ同じで、sqlを引数にしてupdateメソッドを呼び出すだけです。
6.3 トランザクション処理のテスト用コード
5.5と同じ方法でトランザクションをテストするコードを追加します。
7.資料
(1)George Franciscus,http://www.ibm.com/developerworks/java/library/j-sr2.html,
Get a better handle on Struts actions, with Spring, Three ways to integrate Struts applications with Spring アクションにserviceを設定するための方法
(2)トランザクションの伝播 ¶5.3
付録
付録 A. springを使う準備
A.1 環境を作る
springをダウンロードする。
解凍する。
spring-framework-2.0.5/dist/spring.jarをlibにコピー
spring-framework-2.0.5/lib/log4j/log4j-1.2.9をlibにコピー
spring-framework-2.0.5/samples/showcases/java5-dao/src/log4j.propertiesをソース(パッケージなし)にコピー
ついでに、commons-logging-1.0.4.jarをlibから削除し、
代わりにspring-framework-2.0.5/lib/jakarta-commons/commons-logging.jarをlibにコピー
これに伴って、eclipseのプロジェクトのlibraryプロパティを変更する。
commons-logging-1.0.4.jarを削除
spring.jarを追加
log4j-1.2.9を追加
commons-logging.jarを追加
A.2 確認
例題1を使って確認してください。
付録B. spring例題2の作成
webapp例題3をベースにして、Initializerクラスの処理のほとんどをspringに移します。
(1)パッケージを作る。${basePackage}.a0202
(2)新パッケージに${basePackage}.a0103の内容をコピーする。
新しいパッケージa0202を作り、webapp例題3をここにコピーします。webapp例題3のパッケージ(a0103)をすべてa0202に変更します。
(3)struts-config.xmlにactionを追加
|
(4)Action1の変更
|
(5)Action2の変更
|
(6)ExampleContextListenerを変更
|
の下に次の文を追加する。jp.co.nsp_ltd.a0202.N03ExampleInitializerでも初期化する。
|
(7)N03ExampleBaseActionを変更
|
(8)N03ExampleInitializerを変更(1)
|
N03ExampleInitializerについてはさらに変更があります。
(9)確認
ここまでの変更を確認します。
http://localhost:8080/${contextbase}/r1_a0202Example.do
(10)applicationContext.xmlの作成
本文を参考にして下さい。
内容はダウンロードしたファイルを参考にして下さい。
(11)N03ExampleInitializerを変更(2)
本文を参考にして下さい。
内容はダウンロードしたファイルを参考にして下さい。
(12)n03Example.propertiesを削除
(13)確認
付録C.spring例題3の作成
アクションへのserviceの設定をspringにまかせます。
plug-inで初期化します。Actionへのserviceの設定をspringにまかせるにはplug-inで初期化する必要があります。
(1)パッケージを作ります。${basePackage}.a0203
(2)新パッケージに${basePackage}.a0202の内容をコピーする。
新しいパッケージa0203を作り、spring例題1をここにコピーします。spring例題1のパッケージ(a0202)をすべてa0203に変更します。
(3)struts-config.xmlにactionを追加
|
(4)struts-config.xml にplug-inの設定を追加
spring コンテキストを初期化するplug-inを設定します。
|
(5)applicationContext_a0203.xmlの作成
applicationContext.xmlをWEB-INFの直下にコピーします。混乱する恐れがあるので名前をapplicationContext_a0203.xmlに変更します。
applicationContext_a0203.xmlの中にあるパッケージ名jp.co.nsp_ltd.a0202をすべてjp.co.nsp_ltd.a0203に変更してください。
serviceとdaoの2か所あります。
(6)action-servlet_a0203.xmlの作成
|
(7)Action1の変更
|
(8)Action2の変更
|
(9)N03ExampleBaseActionの変更
serviceを設定するメソッド(setter)を追加します。
|
(10)N03ExampleInitializerを削除
付録D.例題4の作成
例題4ではトランザクション処理を追加します。
(1) MS ACCESS用DBを作ります。
textのデータをMS ACCESSに複写する手順は次の通りです。
textのデータをEXCELに複写します。(先頭の行を列名の行にします)
MS ACCESSを立ち上げます。
ファイル/新規作成 ファイル名はtestDb.mdbとします。
データベースウインドウの新規作成をクリック
テーブルのインポートをクリック
インポート元のEXCELのファイルを選択し、さらにテーブルを選択
ワークシートインポートウイザードの中で
「ワークシート」をチェック
「先頭行をフィールド名として使う」をチェック
「新規テーブルに保存する」をチェック
「主キーを設定しない」をチェック
インポート先テーブル名 「table7」
(2)DbAccessorDiTxの作成
DbAccessorDiを次の手順で変更してDbAccessorDiTxを作成します。
commit/rollbackの処理を取り除きます。
Exception発生時いったんそれをキャッチしてUnsupportedOperationExceptionを投げる処理にします。トランザクション処理をするためにはエラーがあったときにServiceでエラーを投げる必要があるためです。
ここは発生したExceptionをそのまま投げてもいいですが、それはchecked Exceptionなので投げることを明示的に宣言する必要があります。したがってシグネチャーを変える必要があります。これまでの例題ではexceptionを投げる仕様ではありませんでしたので、その部分の変更が必要になります。
setAutoCommit(false)を入れます。(デフォルトはtrueに設定されています)
重要:dataSoutce.getConnection()をDataSourceUtils.getConnection(dataSource)に変える(トランザクション処理をspringに頼るため)。connectionを独自で取るとトランザクションの処理が正しく出来なくなります。
conn.close()を取る(理由:同上)
(3)context.xmlの変更(1)
トランザクション処理のプロクシとトランザクション管理に関する設定を追加します。
|
(4)context.xmlの変更(2)
textを使ったDBMSはトランザクションの処理ができませんので、MS ACCESSに変更します。
|
MS ACCESS でなくHSQLDBに変更する場合は次の内容です。
|
(5)context.xmlの変更(3)
dbAccessorのタイプをDbAccessorDiTxに変更する。
|
(6)N03ExampleServiceの変更
addRecordToErrorメソッドを追加します。このメソッドはDaoで例外を投げる処理内容にします。rollback処理を確認する目的です。
addRecordを使って2レコードを追加する処理を入れます。最初のレコードは正常に追加できる内容で、第2のレコードはデータの型が不正で追加できない内容とします。一部を示します。
|
これに伴って、IServiceにaddRecodを追加します。
(7)テスト
バッチでテストします。
(8)初期化クラスを作る。
a0202.N03ExampleInitializerをa0204にコピーします。
keyToServiceImplを変更します。
|
(9)N03ExampleBaseActionの変更(1)
初期化処理を起動する処理を作ります。
|
初期化済みか否かを判断して、まだならば初期化する処理を追加します。
|
(10)N03ExampleBaseActionの変更(2)
例外をキャッチする処理を入れます。
RuntimeExceptionだけキャッチします。
(11)Action1の変更
|
(12)Action2の変更
|
(13)ErrorActionの追加
addRecordToErrorを呼び出すActionを追加します。
|
(14)struts-configの変更(1)
アクションを追加
|
typeがorg.springframework.web.struts.DelegatingActionProxyではないことに注意してください。
(13)struts-configの変更(2)
エラーがあった場合のglobal forwardを入れます。
|
付録 E.例題5の作成
JdbcTemplateを使ったトランザクション処理を追加します。
(1)Daoの変更
JdbcTemplateを使います。JdbcTemplateはDbAccessorに相当する機能を持っているのでDbAccessorを使わないでJdbcTemplateを使う処理に変更します。DbAccessorの場合と違って、JdbcTemplateをDaoが継承する形にします。
継承を定義する部分とマッパーを使っている部分だけ記載します。
|
(2)BaseActionを変更する。
a0202.N03ExampleInitializerをa0204にコピーします。
keyToServiceImplを変更します。
|
(3)初期化クラスを作る。
a0202.N03ExampleInitializerをa0204にコピーします。
keyToServiceImplを変更します。
|
(4)Action1の変更
|
(5)Action2の変更
|
(6)applicationContextを変更します。
DbAccessorを削除します。
DaoのpropertyのdbAccessorを削除し、dataSourceを追加します。
|
(7)struts-configを変更します。
アクションを追加します。エラーの処理は前の例題のものを共用できますので変更は不要です。
|
以上