トランザクションのコードを書かないようにするには?(宣言的トランザクション) | Java Springの逆引きメモ

Java Springの逆引きメモ

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

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


宣言的トランザクションを使用すると、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のトランザクション機能について