@Transactional
をDAO
クラスまたはそのメソッド、あるいはその両方に配置する必要がありますか。それとも、DAOオブジェクトを使用して呼び出しているServiceクラスに注釈を付けることをお勧めしますか。それとも両方の「レイヤー」に注釈を付けることは意味がありますか?
トランザクションはサービス層に属していると思います。それは作業単位とユースケースについて知っているものです。 1つのトランザクションで一緒に作業する必要がある複数のDAOがサービスに注入されている場合、それは正しい答えです。
一般的に私は、トランザクションは通常サービスレベルで開始されると言う他の人たちに同意します(もちろんあなたが要求する粒度によります)。
ただし、その間に、呼び出し側でトランザクションの開始を忘れた場合にエラーを検出する方がはるかに簡単であるため、DAO層(およびトランザクションを開始することはできませんが既存のものを必要とする他の層)に@Transactional(propagation = Propagation.MANDATORY)
を追加し始めました。 (例:サービス) DAOに必須伝播のアノテーションが付けられている場合は、メソッドが呼び出されたときにアクティブなトランザクションがないことを示す例外が発生します。
私はまた、このアノテーションについてすべてのビーン(ビーンポストプロセッサ)をチェックし、サービス層に属さないビーンに必須以外の伝播を伴う@Transactional
アノテーションがある場合に失敗する統合テストを持っています。このようにして、間違った層でトランザクションを開始しないようにしています。
トランザクションアノテーションは不可分のすべての操作の周りに配置されるべきです。
たとえば、あなたの電話は「パスワード変更」です。それは2つの操作から成ります
したがって、上記で、監査が失敗した場合、パスワードの変更も失敗するはずですか?もしそうなら、トランザクションはおよそ1と2になるはずです(つまりサービス層で)。電子メールが失敗した場合(おそらく失敗しないように何らかの安全策が講じられているはずです)、パスワード変更と監査をロールバックする必要がありますか?
これらは、@Transactional
をどこに置くかを決めるときに尋ねなければならない種類の質問です。
従来のSpringアーキテクチャの正しい答えは、他の人がすでに説明した理由から、トランザクションのセマンティクスをサービスクラスに置くことです。
春に出現しつつある傾向は ドメイン駆動設計 (DDD)に向かっています。 Spring Roo はその傾向をうまく表しています。考えは、ドメインオブジェクトPOJOを典型的なSpringアーキテクチャ(通常それらは 貧血性 )よりはるかに リッチ にすること、そして特にトランザクションと永続性の意味をドメインにすることです。オブジェクトそのもの。必要なのが単純なCRUD操作だけである場合、WebコントローラはドメインオブジェクトPOJO(これらはこのコンテキストではエンティティとして機能しています)を直接操作し、サービス層はありません。ドメインオブジェクト間で何らかの調整が必要な場合は、従来どおり@Transaction
を使用してそれを処理するサービスBeanを作成できます。ドメインオブジェクトのトランザクション伝播をREQUIRED
のようなものに設定して、ドメインオブジェクトがサービスBeanで開始されたトランザクションなどの既存のトランザクションを使用するようにすることができます。
技術的にはこのテクニックはAspectJと<context:spring-configured />
を利用します。 Rooは、AspectJの型間定義を使用して、エンティティのセマンティクス(トランザクションと永続性)をドメインオブジェクトのもの(基本的にはフィールドとビジネスメソッド)から分離します。
通常の場合は、サービス層レベルで注釈を付けることですが、これは実際には要件によって異なります。
サービス層に注釈を付けると、DAOレベルに注釈を付けるよりもトランザクションが長くなります。同時トランザクションは互いの変更を見ることができないため、問題を引き起こす可能性があるトランザクション分離レベルに応じて。繰り返し読みます。
DAOに注釈を付けると、トランザクションができるだけ短くなりますが、サービス層が公開している機能が単一の(ロールバック可能な)トランザクションで実行されないという欠点があります。
伝播モードがデフォルトに設定されている場合、両方のレイヤに注釈を付けることは意味がありません。
@Transactional
を@Service
レイヤーに置き、トランザクションをさらに最適化するためにrollbackFor
any exceptionおよびreadOnly
を設定します。
デフォルトでは@Transactional
はRuntimeException
(未チェックの例外)のみを検索し、rollbackをException.class
(Checked Exceptions)に設定することであらゆる例外をロールバックします。
@Transactional(readOnly = false, rollbackFor = Exception.class)
チェック済み例外と未チェック例外 を参照してください。
それとも両方の "layer"にアノテーションを付けるのは意味がありますか? - service layerとdao layerの両方にアノテーションを付けるのは意味がありませんDAOで伝播「必須」を使用して、サービス層からDAOメソッドが常に呼び出される(伝播される)ようにしたい。これは、DAOメソッドがUIレイヤー(またはコントローラー)から呼び出されることを制限します。また、特にDAO層を単体テストする場合は、DAOに注釈を付けることで、トランザクション機能のテストも確実に行われます。
また、Springは具象クラスでのみアノテーションを使用し、インターフェースでは使用しないことをお勧めします。
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
データベースレベルのトランザクション用
ほとんどの場合、DAOではメソッドレベルで@Transactional
を使用していました。そのため、設定はメソッド用にすることも、デフォルトを使用することもできます(必須)。
データフェッチを取得するDAOのメソッド(select ..) - @Transactional
は必要ありません。トランザクションインターセプタやAOPプロキシを実行する必要があるため、これによってオーバーヘッドが生じる可能性があります。
挿入/更新を行うDAOのメソッドは@Transactional
を取得します。
transctional に関する非常に良いブログ
アプリケーションレベル -
予期しないエラーが発生した場合にロールバックできるように、ビジネスロジックにトランザクションを使用しています
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
通常、トランザクションをサービス層に置くべきです。
しかし、前述したように、操作のアトミック性は、注釈が必要な場所を教えてくれます。そのため、Hibernateのように、オブジェクトに対する単一の "save/update/delete/... modification"操作が複数のテーブルの複数の行を変更する可能性がある(オブジェクトグラフのカスケードのため)場合、もちろん、この特定のDAOメソッドに関するトランザクション管理もあるべきです。
@Transactional
注釈は、分離できないすべての操作の周りに配置する必要があります。 @Transactional
トランザクション伝播の使用は自動的に処理されます。この場合、別のメソッドが現在のメソッドによって呼び出された場合、そのメソッドには進行中のトランザクションに参加するオプションがあります。
例を挙げましょう。
2つのモデル、つまりCountry
とCity
があります。 Country
およびCity
モデルのリレーショナルマッピングは1つのCountry
モデルに似ています。複数の都市を持つことができるため、マッピングは次のようになります。
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
ここで、国は複数の都市にマップされ、Lazily
を取得します。したがって、データベースから国オブジェクトを取得するときの@Transactinal
の役割は、国オブジェクトのすべてのデータを取得しますが、都市を取得しているため、都市のセットは取得しませんLAZILY
。
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
国オブジェクトから都市のセットにアクセスする場合、このセットのみで作成されたセットのオブジェクトは@Transactional
を使用するセットの値を取得するためのデータで初期化されないため、そのセットでnull値を取得します。
//with @Transactional
@Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
Object object = country.getCities().size();
}
基本的に@Transactional
は、エンドポイントとの接続を閉じることなく、単一のトランザクションで複数の呼び出しを行うことができるサービスです。
サービス層でそれを持っている方が良いです!これは昨日私が遭遇した記事の一つで明確に説明されています!こちらが あなたがチェックアウトできるリンク です!
まず最初に、トランザクションを使用する必要がある場所を定義しましょう。
正しい答えは、一連のアクションが1回のアトミック操作としてまとめて終了するようにする必要がある場合、またはいずれかのアクションが失敗しても変更が行われないようにする必要がある場合です。
ビジネスロジックをサービスに組み込むことはよく知られている方法です。そのため、サービスメソッドには、単一の論理作業単位として実行する必要があるさまざまなアクションを含めることができます。もしそうなら - そのようなメソッドは、Transactionalとしてマークされなければなりません。もちろん、すべてのメソッドがそのような制限を必要とするわけではないので、サービス全体をtransactionalとしてマークする必要はありません。
さらにもっと - @ Transactionalがメソッドのパフォーマンスを低下させる可能性があることを忘れないでください。全体像を見るためには、トランザクション分離レベルを知っている必要があります。これを知っていると、必ずしも必要ではない場合に@ Transactionalを使用しなくても済みます。
サービス層は、ここに存在するほとんどのビジネスロジックとして@Transactional
アノテーションを追加するのに最適な場所です。詳細レベルのユースケース動作が含まれています。
これをDAOに追加し、serviceから2つのDAOクラスを呼び出すことにします。1つは失敗し、もう1つは成功します。この場合、@Transactional
がサービス上にない場合、1つのDBがコミットし、他はロールバックします。
したがって、私のお勧めはこのアノテーションを賢く使い、Serviceレイヤでのみ使うことです。
理想的には、Service layer(Manager)はあなたのビジネスロジックを表しているので、@Transactional
というアノテーションを付ける必要があります。サービスメソッドにN個のDAO操作がある状況を想定しましょう。あなたの最初のDAO操作が失敗した場合、他の人はまだ合格するかもしれず、あなたは矛盾したDB状態になるでしょう。注釈サービス層はそのような状況からあなたを救うことができます。
@Transactional
はビジネスロジックを含んでいるのでサービスレイヤで使用されるべきです。 DAO層には通常、データベースのCRUD操作しかありません。
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
Spring doc: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
メソッドレベルでサービス層に@Transactional
を使うのが好きです。
@Transactional
は、サービス層でコントローラ層(@Controller
)を使用して呼び出され、サービス層はDAO層への呼び出し(@Repository
)、すなわちデータベース関連の操作を使用する。
DAOとサービス層の間の別の中間層に@ Transactionalを保持することをお勧めします。ロールバックは非常に重要なので、すべてのDB操作を中間層に置き、ビジネスロジックをサービス層に書くことができます。中間層はあなたのDAO層と相互作用します。
これは、ObjectOptimisticLockingFailureExceptionのような多くの状況で役に立ちます。 - この例外は、トランザクションが終了した後にのみ発生します。だから、あなたはそれを中間層でキャッチすることはできませんが、あなたは今あなたのサービス層でキャッチすることができます。 Serviceレイヤに@Transactionalがある場合、これは不可能です。あなたはコントローラに引っ掛かることができますが、コントローラはできるだけきれいでなければなりませんが。
あなたがすべての保存、削除、および更新オプションを完了した後に別々のスレッドでメールやSMSを送信している場合、あなたはあなたの中間層でトランザクションが完了した後にインサービスでこれを行うことができます。繰り返しになりますが、サービスレイヤで@Transactionalを指定した場合、トランザクションが失敗してもメールが送信されます。
中間の@Transactionレイヤを持つことはあなたのコードをより良くそして扱いやすくするのを助けるでしょう。さもなければ、あなたがDAO層で使うならば、あなたはすべての操作をロールバックすることができないかもしれません。 Serviceレイヤで使う場合、場合によってはAOP(アスペクト指向プログラミング)を使う必要があるかもしれません。