Java Springの逆引きメモ -5ページ目

Java Springの逆引きメモ

JavaのSpring frameworkのメモを書いていきます!
初心者の勉強ノートなので間違いがあるかもしれませんが、何かヒントになることがあれば幸いです。

前の記事 で見たプログラムによるトランザクションを見ていきます。


実はSpringが提供するプログラムによるトランザクション管理は、2種類あります。

まずはその一つ目です。


例によって、サンプルを中心に見ていきます。



【ビジネスロジック】

public interface MemberService{ List<String> find(String name); } //setter/getterは記述を省略しています public class MemberServiceImpl implements MemberService{
private MemberDao memberDao; private TransactionTemplate transactionTemplateRequired;

void doSomething(String name){
//トランザクションを管理する (返り値が無い場合) transactionTemplateRequired.execute(new TransactionCallbackWithoutResult() {
//このメソッド内で実行したSQLがトランザクション管理されます。 protected void doInTransactionWithoutResult(TransactionStatus status) { memberDao.insert(); memberDao.update(); }
}); }
}




【Spring設定ファイル(AppricationContext.xml)】


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans "

xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

xmlns:aop ="http://www.springframework.org/schema/aop "
xmlns:tx ="http://www.springframework.org/schema/tx "
xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd ">


<!-- Data Source (例えばPostgres)--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.postgresql.Driver"/> <property name="url" value="jdbc:postgresql://localhost/XXXX" /> <property name="username" value="XXX" /> <property name="password" value="XXX" /> </bean>
<!-- DAO --> <bean id="memberDao" class="dao.MemberDaoImpl"> <property name="dataSource" ref="dataSource" /> </bean> <!-- ビジネス --> <bean id="memberService" class="business.service.MemberServiceImpl"> <property name="transactionTemplateRequired" ref="transactionTemplateRequired" /> </bean>


<!-- トランザクション管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>


<!-- トランザクション処理用クラス。 --> <bean id="transactionTemplateRequired" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager" /> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" /> </bean>

</beans>




【説明】


<やること>

MemberServiceImplクラス内の、doSomethig()メソッドを見て下さい。

再掲:


//トランザクションを管理する (返り値が無い場合)
transactionTemplateRequired.execute(new TransactionCallbackWithoutResult() {

 protected void doInTransactionWithoutResult(TransactionStatus status) {
   //このメソッド内で実行したSQLがトランザクション管理されます。
   memberDao.insert();
   memberDao.update();
 }
});


setterでspringの設定ファイルから設定したtransactionTemplateRequiredを使用しています。

executeメソッドを呼び出していますが、引数はコールバッククラスTransactionCallbackWithoutResultです。

このクラスは無名クラスにして、メソッドdoInTransactionWithoutResultを実装しています。


結局、このメソッド内にトランザクション処理を書けばいいだけです。にひひ




<トランザクション属性の設定>

さて、宣言的トランザクションなどの他のトランザクション処理では、いつトランザクションを開始するかを指定できました。

このサンプルではどのように指定しているでしょうか?


Springの設定ファイルのtransactionTemplateRequiredを見てください。

再掲:

 <!-- トランザクション処理用クラス。 -->
 <bean id="transactionTemplateRequired"
  class="org.springframework.transaction.support.TransactionTemplate">
   <property name="transactionManager" ref="transactionManager" />
   <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" />
 </bean>


propagationBehaviorNameで設定しているのが分かるでしょうか?

PROPAGATION_REQUIREDになっているので、トランザクションを開始していなければ開始します。

この設定値の詳細は、以下のリンクの一覧表を参照してください。

 ・トランザクションのコードを書かないようにするには?(宣言的トランザクション)


 先頭に、PROPAGATION_が付きますが同じになっているのが分かると思います。


これだけで終わりです!


ここでは、1つのクラスMemberServiceImplだけにtransactionTemplateRequiredを設定していますが

使いまわして、他のクラスに設定してもOKです。

ただし、注意点があります。




TransactionTemplateの使用上の注意点】

TransactionTemplateは、setterでいくつかのプロパティ値を設定ができます。

しかし、プロパティ値はスレッドセーフではないので、途中で値を変えられると他のスレッドにも影響を与えます。

その代わり、executeメソッドはスレッドセーフです。

ですので、setterでプロパティ値さえ変更しなければ、使いまわしOKなのですビックリマーク
やっぱり、クラスごとに設定したりするのは大変なので、使いまわしできるのは便利ですね。




TransactionTemplateによるトランザクションの使いどころ】

使いどころは、メソッド内で複数のトランザクションを処理しないといけない場合だと思います。

もし、1つメソッドに対して1つのトランザクションをかけるなら、アノテーションによるトランザクション、宣言的トランザクションでいいかと思います。


例えば以下のようなトランザクション処理を、宣言的、もしくはアノテーションによるトランザクション処理で実現しようとすると、かなりやりづらいはずです。


例:サンプルイメージです


public void doSomething(){

  

  try{

    //1つめのビジネスロジックの呼び出し

    transaction.begin();

    memberService.insert(true);

  }catch(Exception e){

    transaction.rollback();

  }

  

  try{

    //2つめのビジネスロジックの呼び出し

    transaction.begin();

    memberService.insert(false);

    transaction.commit();

  }catch(SQLException e){

    transaction.rollback();

  }catch(Exception e){//SQLエラー以外はコミットする

    transaction.commit();

  }

}



メソッド内で2つが別々のトランザクションになっているので、

doSomethingメソッドに宣言的トランザクションをかけられません。


また、memberService.insert()メソッドにかけることもできません。

同じメソッドの呼び出しが2回行われていますが、それぞれトランザクション処理のやり方が違うからです(catchのところ)。


こんなときはプログラムによる処理をするほうが楽です。


また、TransactionTemplateは使い回しができるので、いくつも同じトランザクション処理をする箇所に使用するといいと思います。


とはいってもそれ程トランザクション処理のパターンは無いかと思います。

ですので、以下のようなものを用意して、プログラム側で適当にインジェクションしていくのがいいかと思います。

 ・PROPAGATION_REQUIREDを設定したTransactionTemplateのbean

 ・PROPAGATION_TESTEDを設定したTransactionTemplateのbean

 ・例外クラス毎にrollback/commitを設定したTransactionTemplateのbean

 などなど




【補足】

サンプルでは、トランザクション処理の結果、返り値が必要ないパターンを書きました。

もし返り値が必要な場合は、以下のようにコールバックするクラスを変更します。


例:

//トランザクションを管理する (返り値がある場合)
Object ret = transactionTemplateRequired.execute(new TransactionCallback() {
 //このメソッド内で実行したSQLがトランザクション管理されます。
 protected void doInTransaction(TransactionStatus status) {
   Object ret = memberDao.insert();
   memberDao.update();

      return ret;
 }
});




簡単ですよね!



参照:

トップ

・Springのトランザクション機能について

Spring本家ドキュメントでのトランザクションの説明

前の記事 で見たアノテーションによるトランザクションを見ていきます。


例によって、サンプルをメインにしてみて行こうと思います。




【Spring設定ファイル(AppricationContext.xml)】


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans "

xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

xmlns:aop ="http://www.springframework.org/schema/aop "
xmlns:tx ="http://www.springframework.org/schema/tx "
xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd ">


<!-- Data Source (例えばPostgres)--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.postgresql.Driver"/> <property name="url" value="jdbc:postgresql://localhost/XXXX" /> <property name="username" value="XXX" /> <property name="password" value="XXX" /> </bean>


<!-- DAO -->
<bean id="memberDao" class="dao.MemberDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>


<!-- ビジネス -->
<bean id="memberService" class="business.service.MemberServiceImpl">
</bean>


<!-- トランザクション管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>


<!-- トランザクションのアノテーションを有効にする -->

<tx:annotation-driven />


</beans>




<ビジネスロジック(アノテーションのサンプルその)>

@Transactional(readOnly=true)

public class MemberServiceImpl{


 public find(Member member){

   

 }


 @Transactional(readOnly=false, rollbackFor=RuntimeException.class)

 public update(Member member){

   

 }

//・・・様々なメソッド

}





【説明】

クラスの先頭でクラス内のメソッドすべてにreadOnlyを設定しています。

そして、さらに、メソッド毎にも@Transactional を設定できています。


アノテーションは、オーバーライドができるようです。


この事情は、クラスを派生させたときにも同じようです。



さて、ほんとんど説明することはないかと思います。

指定したクラス、またはメソッド毎にトランザクションを指定できます。


宣言的トランザクションがアプリケーション全体を相手にしているのに対して、

アノテーションは1つのクラス、1つのメソッドを相手にする感じですかね。


上記では、updateメソッドだけ、書き込みOKになっています。



<アノテーションを有効にする方法>

実は@Transactional をコードに記述すればトランザクションが行われるわけではありません。

Springの設定ファイルに以下の記述をする必要があります。


① <tx:annotation-driven />


② beanのtransactionManagerを記述する。


これだけです!音譜


 上記①のタグの属性はAOPの設定とほぼ同じです。

 基本的にはinterfaceについてのみアノテーションが適用されます。

 classについても適用したい場合は、proxy-target-class="true"に設定してください。

 詳しくは、・DIxAOPの機能について 参照。



【設定について】

実は、内容は宣言的トランザクションと同じです

ですので、宣言的トランザクションのタグとアノテーションの対応さえを分かればすぐ使えると思います。

こちらの記事 の一覧表とあわせてみていただければ分かるかと思います。



<宣言的トランザクションとの対応表>

tx:methodのタグ属性 @Transactionalでの名称 アノテーションの備考
propagation propagation Propagation.REQUIREDのように列挙型を設定する
isolation isolation Isolation.DEFAULTのように列挙型を設定する
read-only readOnly true/false
rollback-for rollbackFor RuntimeException.classのようにクラスを設定する。
no-rollback-for noRollbackFor 同上
timeout timeout






宣言的トランザクションとアノテーションのトランザクションは同じなので、どちらかを覚えればすぐ使用できるかと思います!



参照:

トップ

・Springのトランザクション機能について

Transactionalアノテーションの例


前の記事 で見た宣言的トランザクションを見ていきます。


宣言的トランザクションを使用すると、beginTransaction()、rollback()、commit()が自動で行われるようになるため、何もトランザクションのコードを書く必要が無くなります音譜

ここではサンプルをメインにしてみて行こうと思います。



【DAO】
public interface MemberDao{
  List<String> find(String name);
}


pulic class MemberDaoImpl implements MemberDao {
  List<String> find(String name){
      //...SQLなどの実行
      return listNames;
  }
}


【ビジネスロジック】
public interface MemberService{
  List<String> find(String name);
}

//setter/getterは記述を省略しています
public class MemberServiceImpl implements MemberService{
  private MemberDao memberDao;

  List<String> find(String name){
     return memberDao.find(name);
  }
}



【Spring設定ファイル(AppricationContext.xml)】


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans "

xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

xmlns:aop ="http://www.springframework.org/schema/aop "
xmlns:tx ="http://www.springframework.org/schema/tx "
xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd ">


<!-- Data Source (例えばPostgres)--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.postgresql.Driver"/> <property name="url" value="jdbc:postgresql://localhost/XXXX" /> <property name="username" value="XXX" /> <property name="password" value="XXX" /> </bean>


<!-- DAO -->
<bean id="memberDao" class="dao.MemberDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>


<!-- ビジネス -->
<bean id="memberService" class="business.service.MemberServiceImpl">
</bean>


<!-- トランザクション管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>


<!-- 宣言的トランザクションの織り込み -->
<aop:config proxy-target-class="false">
<aop:advisor pointcut="execution(* business.service.*(..))" advice-ref="txAdvice" />
</aop:config>

<tx:advice id="txAdvice" >
<tx:attributes>
<tx:method name="find*" read-only="true" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>

</beans>






【説明】
DAOとビジネスロジックは、通常のSpringのお作法どおり書いていますので説明は省きます。

ここではfindというメソッドのみを作成しましたが、もっといろいろ追加しても当然OKです!


パッケージbusiness.service 内に存在するクラスのメソッドについて勝手にトランザクションの開始とcommit/rollbackをしています。 


では、Springの設定ファイルを中心に見ていきます。



<トランザクション管理クラス(transactionManager)>

このbeanの設定が、トランザクション管理を実際に担当するクラスの設定になります。
bean名(id)は、通常はtransactionManagerにしておきます。



<トランザクションのアドバイス(tx:advice)>

宣言的トランザクションでは、AOP を使用して処理を実現します。

そのため、アドバイスとよばれるAOPの機能を使用します。

ただし、Springではタグ名にtx:をつけて、トランザクション専用のアドバイスという管理になっています。


ここで実は省略が入っています。

どこにもtransactionManagerというbean名が記述されていません。

実は、Springの設定ファイルにこの名前でbeanが設定されていればそれを使用するようになっているので記述を省略できるのです。


tx:methodタグの属性:
name

トランザクション開始・終了の管理をするメソッド名をしていします。

ワイルドカードを使用できます。

propagation

トランザクション属性を指定します。

トランザクション管理のメソッドがネストしているときに特に役に立ちます。


【設定可能な値】
- REQUIRED・・トランザクションが開始されていなければ開始する
- SUPPORTS・・トランザクションが開始されていればそれを利用。開始されていなければ利用しない。
- MANDATORY・・トランザクションが開始されていなければ例外を発生。
- REQUIRES_NEW・・常に新しいトランザクションを開始する。
- NOT_SUPPORTED・・トランザクションを利用しない。
- NEVER・・トランザクションを開始していれば例外を発生。
- NESTED・・ネストしたトランザクションを開始する。

isolation

トランザクションの独立レベルを設定します。

ただし、この設定はDBの仕様に依存するため、使用するDBの仕様をよく確認する必要があります。


【設定可能な値】

- DEFAULT・・DBが提供するデフォルトの独立性レベル。
- READ_UNCOMMITTED・・独立性は気にしない。
- READ_COMMITTED・・他のトランザクションが変更したがまだコミットしていないデータは読めない。
- REPEATABLE_READ・・他のトランザクションが読み出したデータは更新できない。
- SERIALIZABLE・・トランザクションを完全に独立させる。

read-only トランザクションが読み取り専用かどうかを設定します。
rollback-for

ここで設定した例外が発生したときにロールバックします。

例外クラスの指定は、カンマ区切りで複数できます。

ロールバックやコミットのタイミングは、nameで指定したメソッドを抜けた後です。

トランザクション管理に指定したメソッドがネストしている場合、一番外側のメソッドを抜けた後です。

no-rollback-for

ここで設定した例外が発生したときはコミットします。

例外クラスの指定は、カンマ区切りで複数できます。

timeout トランザクション処理のタイムアウト時間(秒)を指定します。



<AOPの織り込み(aop:config)>

これは通常のAOPの設定です。

上記で定義したアドバイスを設定しているだけです。


以下を参照してください。

・DIxAOPの機能について




【サンプルの動作】

サンプルでは、トランザクション管理する対象メソッドとして、find*が設定されています。


設定の再掲:

  <tx:method name="find*" read-only="true" />


つまり、find、findTargetなど、メソッド名がfindで始まるものが対象になります。

そしてポイントカットでbusiness.service.*(..)を指定しているので、

MemberServiceImplクラスなどが対象になります。


そして、find*は、read-onlyとなっているため、もし更新SQLが実行されるとエラーになるはずです。


またその下のタグで*が設定されているので、find*に引っかからなかったメソッドはすべてREQUIRED になっています。

つまり、updateTarget, insertTargetなどのメソッドがあれば、トランザクション開始する仕様です。


 <tx:method name="*" propagation="REQUIRED" />




【ロールバックの動作】

MemberServiceImplに、以下のメソッドがあったとします。

上記のSpringの設定どおり、REQUIRED になるため、例外が発生するとロールバックします。

これをもう少し詳しく見てみます。



pulic void insertTarget(Member member){

  //・・・処理

}


pulic void updateTarget(Member member){

  insertTarget(member)

  //・・・その他のDB処理

}


トランザクション管理のメソッドがネストしているのが分かるでしょうか?

この場合は、どのようにロールバックするでしょうか?


insertTargetメソッド内で例外が発生した場合

 insertTargetメソッドを抜け出した後にロールバックします。

 REQUIRED の場合は、イメージとしてはinsertTargetメソッドが以下のように書き換えられた感じです。


 try{

   transaction.begin();

   insertTarget(member);

   transaction.commit();

 }catch(Exception e){

   transaction.rollback();

 }



updateTargetメソッド内で例外が発生した場合

 updateTargetメソッドを抜け出した後にロールバックします。

 トランザクション属性にREQUIRED を指定した場合、イメージとしては以下のような感じです。


 try{

   transaction.begin();

   updateTarget(member);

   transaction.commit();

 }catch(Exception e){

   transaction.rollback();

 }




 ※insertTarget()にも当然、トランザクションの処理が織り込まれていますが、

 すでにupdateTarget()の方でトランザクションを開始していますので、insertTarget()に織り込まれた

 トランザクション開始は無視されます。

 もし、トランザクション属性にNESTEDを指定していれば、ネストしたトランザクションがinsertTarget()でも

 開始されるはずです。




こうして、methodタグで指定したメソッド毎にトランザクションの開始・終了の設定ができます。

これが宣言的トランザクションです。


ほぼ、これだけで十分だと思います。


その他の機能は、別の記事で書いていこうと思います。

以下の記事を参考にしてみてください。

 ・Springのトランザクション機能について




参照:

トップ

・Springのトランザクション機能について



SpringにはDBのトランザクションの管理を包括的に扱う機能があります。


トランザクションの問題は以外と難しい問題です。

一般的な問題点と、Springでの解決方法を簡単に書いてみます。



【トランザクションの一般的問題点】

1.DBにアクセスするたびに、beginTransaction, commit, rollbackを記述する必要がある。

  多人数で開発する場合、記述漏れをする人がいるかもしれない


2.WEBの場合、リクエストを受け取るController部分(例えばStrutsのAction)の基底クラスで

  beginTransaction, commitなどすれば記述漏れの心配が少なくなるが別の問題があります。

  ヘルパークラスなどを作成し、static変数にConnectionなどをスレッドローカルで保存しておき、

  SQL実行用のstaticメソッドをDAOの中で呼び出す必要がある。

    例: TransactionHelper.exceute("select * member");

  

  この問題点は、DAOのソースが絶対にTransactionHelperクラスを使用しなければならなくなり、

  他のトランザクション管理に切り替えたい場合に、TransactionHelperクラスを大きく変更する必要が

  あることです。

  何かフレームワークを使用したくなったときは最悪、上記のメソッド呼び出しの記述をすべて書き換えないと

  いけないかもしれません。

  つまり、柔軟性が低くなるのです。


3.さらに、上記2の方法は1つのリクエストに対して1回のトランザクションとなっています。

 1つのリクエスト内で2回トランザクションを開始するような場合、破綻してしまいます。



他にも細かな問題点はあるかと思います。

プレゼン、ビジネスロジック、DAOと、うまく設計を分けても、トランザクション処理だけは3つの間を横断してしまうことが多いと思います。

真面目な設計者の悩みどころです。



【Springが提供してくれる機能】

(1).宣言的トランザクション

 プログラム上からトランザクションの処理の記述を無くせます。

 メソッド名を指定すると、指定されたメソッドの開始時に勝手にトランザクション開始し、

 メソッド終了時に勝手にコミットしてくれます。

 また、例外が発生したときは勝手にロールバックしてくれます。

 しかも設定はSpringの設定ファイルに数行記述するだけですビックリマーク


(2).アノテーションによるトランザクション管理

 宣言的トランザクションはまとめて一気に設定するイメージです。

 つまり、指定したパッケージ、クラス内の同じメソッド名であればすべて同じ設定になります。

 それに対してアノテーションを使う方法はメソッドごとにアノテーションを記述するため、

 個別にトランザクション管理ができます。


(3).ネストされたメソッドも1つのトランザクションで処理できる (再利用性が高くなる)

 例えば、メソッドA()、メソッドB()があったとして、2つともSpringのトランザクションの対象にしたとします。

 そして、メソッドA()の中からメソッドB()を呼んだとします。

 メソッドB()だけを呼び出した場合、以下のようにトランザクションがかかります。


  ←トランザクション開始

  メソッドB()

  ←トランザクション終了


 メソッドAが呼ばれた場合は、以下のようになります。


  ←トランザクション開始

  メソッドA()開始

    メソッドB()の呼び出し

  メソッドA()終了

  ←トランザクション終了


 Springトランザクション内部で、すでにトランザクションを開始したかを管理しているために上記のようなことが

 できます。


(4).プログラムでのトランザクション管理

 上記(1)、(2)のトランザクションの管理の他に、もっと自由にできる方法も用意されています。

 上記(1)ではアプリケーション内すべての指定したメソッドに1つのトランザクション管理の方法を提供します。

 上記(2)ではクラスもしくは、メソッド1つずつに1つのトランザクション管理の方法を提供します。

 

 プログラムでのトランザクション管理は、プログラムで管理を記述するため、1つのメソッド内でも複数の

 トランザクション管理を提供できます。



上記の詳しい内容は、また別の記事で見ていこうと思います。



【問題点との関係】

上記の機能(1)だけで先にあげた問題点が解決しているのが分かるでしょうか?

宣言的トランザクションは、AOP により自動的にトランザクション管理がプログラムに織り込まれます。

織り込みのためのプログラムはSpringが用意しているためプログラム不要です。


そもそもプログラム不要のため、問題点1と2は解決します。


問題点3の複数回のトランザクションをしたい場合。


ビジネスロジックのメソッドは通常、1つの機能ブロック単位で設計されるため、それより細かなトランザクション管理は発生しないはずです。

つまり、通常であれば複数回のトランザクションを行いたい場合は、ビジネスロジックの呼び出し単位のはずです。

宣言的トランザクションは、通常、ビジネスロジックのメソッド毎にかけます。

(指定すればどこにでもかけられますが)

そうすれば、ビジネスロジックが呼び出されるたびに勝手にトランザクションが開始・終了するため、問題点3も勝手に行われます。


また、機能の(4)を使用すれば自由にトランザクション管理できるため、最悪の場合の逃げ道にできます。


とても勉強して使ってみる価値のある機能だと思いませんか?




【まとめ】

プログラム上からトランザクションのコードを無くせるのはすごくないですかえっ


他の記事で見ていきますが、機能もかなり単純化されているので分かりやすいです。

しかも、上記の機能は同時に使っても、上記(3)の機能は生きています。

ふつう、トランザクションの開始・終了処理をメソッド内に書いてしまい、そのメソッドの呼び出し元でもトランザクションの開始・終了を書いてしまうと、rollbackがうまく働かなくなります。

そうなると、あるメソッドはここでは使用できるけど、ここでは使用できない、などの制限ができ、使用時にいろいろ関係性を考えてコードを書かないといけません。

それを気にせずにコードを書けるのは大きなメリットです。

メソッドの再利用性が高くなってますよね!



ぜひ、うまく使って再利用性の高さと、効率化を量りましょう!




参考:

トップ

・Springのトランザクション機能について
・トランザクションのコードを書かないようにするには?(宣言的トランザクション) (←設定値の説明あり)

・アノテーションによるトランザクション管理
・プログラムによるトランザクション管理(TransactionTemplate)

・プログラムによるトランザクション管理(PlatformTransactionManager)




WEBを動かすときに、定期的に何か処理をしたくなるときがありますよね。

例えば、他のWEBに定期的にアクセスして、自分のDBと値を同期させるとか。


定期的なバッチ処理をWEBを動かすJavaでやりたくなる理由はいくつかあると思います。


 ・WEBで使用しているDAOやビジネスロジックのクラスを利用したい

 ・WEB上でメモリに展開しているデータを使用したい


など。

この場合、linuxのクーロンや、Windowsのタスクでやろうとすると、WEBのWAR内にあるクラスを使うのは一苦労です。

クラスをバッチ用にコピーしてもいいのですが、こうすると2重管理になってしまいます。


こんなときはスケジューラ機能が便利です。

WEBでもバッチでも持っているリソース(クラスやデータ)を共同利用するには、少しクラス設計を工夫する必要がありますが、簡易な定期処理を起動することはSpringのスケジューラ機能が助けになります。




【サンプル】

<beans xmlns="http://www.springframework.org/schema/beans
"
 xmlns:tx="http://www.springframework.org/schema/tx
"
 xmlns:aop="http://www.springframework.org/schema/aop
"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
"
 xsi:schemalocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
 " />



<!-- バッチサービス --> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="masterDataReloadTrigger" /> </list> </property> </bean>

<!-- 起動するタイミング(時刻)の指定 --> <bean id="masterDataReloadTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="masterDataReloadJob" /> <property name="cronExpression" value="0 */30 * * * ?" /> </bean>
<!-- 実際に起動する処理の指定 -->
<bean id="masterDataReloadJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
  <property name="targetObject" ref="mappingDataService" />
  <property name="targetMethod" value="reload"  />
</bean>
<!-- 実際の処理をする自作クラスを設定する。 ここでは具体的なクラスを記述しませんがreload()メソッドを持っていることにします。 --> <bean id="mappingDataService" class="my.business.MappingDataService"> </bean>

</beans>


【説明】

上記のように、Springの設定をするだけでバッチを起動します。

しかも、単純にメソッドを起動するだけなら、特殊なクラスを継承したりする必要もありません。


<必要なフレームワーク>

Springバッチはquartzというフレームワークを必要とします。

これは、定期起動処理をするフレームワークです。

「じゃあ、これをそのまま使用すればいいじゃん!」って思うかもしれませんよね。

でも、quartzを利用するには様々なクラスを継承する必要があり、バッチ起動のためだけのクラスをいくつも作る必要があります。

それを隠蔽して簡易に利用できるようにしたのがSpringバッチです!



<起動時刻の設定>

masterDataReloadTriggerのbeanを見てください。

上記では、毎時0分、30と、30分おきに起動するようにしています。

<property name="cronExpression" value="0 */30 * * * ?" />

linuxなどのクーロンの設定をしたことがある人はおなじみの書き方だと思います。

 「秒 分 時 日 月 曜日 年」

という順番で記述します。

記法例は、以下のとおり。


記述例 説明
0 0 * * * 数値を指定する。毎時0分0秒に起動
0 */30 * * * /を使用する。割り算をして余りがないときに起動。
左の場合、0分、30分。
0 10,15 * * * カンマ区切りで記述する。左の場合、毎時10分0秒、15分0秒に起動する。
0 0 0 1 * ? */2 ?を使用する。?は日と曜日に使用できますが、*とほぼ同じ意味です。ただ、年を指定する場合に曜日も指定しないといけなくなります。曜日を指定したくないのに*を記述すると毎日起動してしまいますので?で回避します。
左の例ですと、偶数年の毎月1日0時0分0秒に起動します。
0 10-15 * * * ハイフンで記述する。左の場合、毎時10分0秒、11分0秒、~15分0秒に起動する。




<実施する処理の設定>

masterDataReloadJobのbeanを見てみてください。

ここで設定しています。

Springバッチが提供するMethodInvokingJobDetailFactoryBeanクラスがその役割を担っています。

設定は簡単で、起動するクラスのbean名と、メソッドを指定するだけです!


そして、こbeanは上で説明したクーロンに設定します。

簡単ですね。



<バッチの管理>

設定の一番最初にあるSchedulerFactoryBeanを見てみてください。

上記で設定したクーロンを指定しているだけです。


 <property name="triggers">
  <list>
   <ref bean="masterDataReloadTrigger" />
  </list>
 </property>


これも簡単ですねニコニコ

しかも、listになっているので複数設定できます。



以上で説明終わりです。

特にquartzと依存関係を持つ必要もないし、特殊なクラスを作る必要もありません。

WEBで作ったビジネスロジックをそのまま再利用できるので、大変助かります!



【補足1】
ここではquartzを使用する方法を紹介しましたが、Java's Timerを使用する方法もあるようです。

Java's TimerはJava標準の機能のようです。



【補足2】

ここからは、1年前に聞いたお話で自分で調べたわけではないので不確かな情報ですが

一応書いておきます。


どうもquartzは、ある処理が終わったら次の処理に行く、とか、複数のバッチ起動のグループがあってお互いに2重起動しないように制御するなどの様々な機能があるようです。

そして、それらの複雑な機能を使用するには、Springは貧弱で5個以上のクラスを作成してあげないといけないようです。ガーン


ですので、単純に1個の処理を起動する場合に利用するのがよさそうな感じがします。

それでも十分使いどころがあるので、便利ですよね。




参照:

・トップ