あなたは配置する必要があります@Transactional
でDAO
、クラスおよび/またはそのメソッドまたはそれはDAOオブジェクトを使用して呼び出しているサービスクラスに注釈を付けること良いですか?または、両方の「レイヤー」に注釈を付けることは理にかなっていますか?
あなたは配置する必要があります@Transactional
でDAO
、クラスおよび/またはそのメソッドまたはそれはDAOオブジェクトを使用して呼び出しているサービスクラスに注釈を付けること良いですか?または、両方の「レイヤー」に注釈を付けることは理にかなっていますか?
回答:
トランザクションはサービス層に属していると思います。作業単位とユースケースについて知っているものです。単一のトランザクションで連携する必要がある複数のDAOがサービスに挿入されている場合は、これが正しい答えです。
一般に、トランザクションは通常サービスレベルで開始される(もちろん、必要な粒度に応じて)と述べている他の人にも同意します。
ただし、その間に@Transactional(propagation = Propagation.MANDATORY)
、DAOレイヤー(およびトランザクションを開始することは許可されていないが、既存のレイヤーを必要とする他のレイヤー)への追加も開始しました。例:サービス)。DAOに必須の伝播の注釈が付けられている場合、メソッドが呼び出されたときにアクティブなトランザクションがないことを示す例外が発生します。
また、このアノテーションについてすべてのBean(Beanポストプロセッサ)をチェック@Transactional
し、サービスレイヤーに属していないBeanに必須以外の伝播を伴うアノテーションがあると失敗する統合テストもあります。このようにして、間違ったレイヤーでトランザクションを開始しないようにします。
@Transactional
Service実装クラスに入れるべき@Transactional(propagation = MANDATORY)
で、DAO(リポジトリ)クラス実装に入れるべきですか?
トランザクションアノテーションは、分離できないすべての操作の周囲に配置する必要があります。
たとえば、あなたの呼び出しは「パスワードの変更」です。それは2つの操作で構成されています
上記のように、監査が失敗した場合、パスワードの変更も失敗する必要がありますか?その場合、トランザクションは約1と2になるはずです(サービス層で)。電子メールが失敗した場合(おそらくこれに何らかのフェイルセーフが必要であるため、失敗しないでしょう)、パスワードの変更と監査をロールバックする必要がありますか?
これらは、をどこに置くかを決めるときに尋ねる必要がある種類の質問です@Transactional
。
従来のSpringアーキテクチャーの正解は、他の人がすでに説明している理由から、トランザクションセマンティクスをサービスクラスに配置することです。
Springの新しいトレンドは、ドメイン駆動設計(DDD)です。Spring Rooはその傾向をよく示しています。アイデアは、ドメインオブジェクトのPOJOを通常のSpringアーキテクチャ(通常は貧弱なアーキテクチャ)よりもはるかに豊かにすること、特にドメインオブジェクト自体にトランザクションと永続性のセマンティクスを配置することです。必要なのが単純なCRUD操作だけの場合、WebコントローラーはドメインオブジェクトのPOJO(このコンテキストではエンティティとして機能します)を直接操作し、サービス層はありません。ドメインオブジェクト間で何らかの調整が必要な場合は、サービスBeanで次のように処理できます。@Transaction
伝統に従って。ドメインオブジェクトのトランザクションの伝播を次のようにREQUIRED
設定して、ドメインオブジェクトがサービスBeanで開始されたトランザクションなどの既存のトランザクションを使用するようにすることができます。
技術的には、この手法ではAspectJとを使用します<context:spring-configured />
。Rooは、AspectJのタイプ間定義を使用して、エンティティのセマンティクス(トランザクションと永続性)をドメインオブジェクト(基本的にフィールドとビジネスメソッド)から分離します。
通常のケースはサービス層レベルで注釈を付けることですが、これは実際には要件に依存します。
サービス層に注釈を付けると、DAOレベルに注釈を付けるよりもトランザクションが長くなります。たとえば、並行トランザクションはお互いの変更を認識しないため、問題を解決できるトランザクション分離レベルに依存します。反復可能な読み取り。
DAOに注釈を付けると、トランザクションができるだけ短くなりますが、サービス層が公開している機能が単一の(ロールバック可能な)トランザクションで実行されないという欠点があります。
伝播モードがデフォルトに設定されている場合、両方のレイヤーに注釈を付けることは意味がありません。
レイヤーに配置し@Transactional
て例外を@Service
設定し、トランザクションをさらに最適化します。rollbackFor
readOnly
デフォルトで@Transactional
は、RuntimeException
(チェックされていない例外)のみを検索します。ロールバックをException.class
(チェックされた例外)に設定することで、すべての例外をロールバックします。
@Transactional(readOnly = false, rollbackFor = Exception.class)
チェック済み例外とチェックなし例外を参照してください。
または、両方の「レイヤー」に注釈を付けることは理にかなっていますか?-サービスレイヤーとdaoレイヤーの両方に注釈を付けるのは意味がありません-DAOで伝播が「必須」のサービスレイヤーからDAOメソッドが常に呼び出される(伝播される)ことを確認したい場合。これは、UIレイヤー(またはコントローラー)からDAOメソッドが呼び出されないようにするためのいくつかの制限を提供します。また、特にDAO層をユニットテストする場合、DAOに注釈を付けると、トランザクション機能についてもテストされます。
propagation=Propagation.REQUIRES_NEW
。それ以外の場合、propogation = mandatoryを含むほとんどの場合、DAOはサービス層によって開始された既存のトランザクションに参加します。
また、Springでは、インターフェースではなく具象クラスのアノテーションのみを使用することをお勧めします。
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
データベースレベルのトランザクションの場合
ほとんど私@Transactional
はDAOのメソッドレベルでのみ使用しているため、構成は特にメソッド用/デフォルトを使用することができます(必須)
データフェッチ(選択..)を取得するDAOのメソッド- @Transactional
これを必要としない場合
、トランザクションインターセプター/およびAOPプロキシも実行する必要があるため、オーバーヘッドが発生する可能性があります。
挿入/更新を行うDAOのメソッドは、 @Transactional
超越に関する非常に良いブログ
アプリケーションレベルの場合-
ビジネスロジックにトランザクションを使用していますが、予期しないエラーが発生した場合にロールバックできるようにしたい
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
Transactional
でJava
通常、トランザクションはサービス層に置く必要があります。
しかし、前に述べたように、操作の原子性は注釈が必要な場所を教えてくれます。したがって、Hibernateのようなフレームワークを使用する場合、オブジェクトに対する単一の「保存/更新/削除/ ...変更」操作により、(オブジェクトグラフを介してカスケードされるため)複数のテーブルの複数の行が変更される可能性があります。もちろん、この特定のDAOメソッドにはトランザクション管理も必要です。
@Transactional
注釈は、分離できないすべての操作の周囲に配置する必要があります。@Transactional
トランザクションの伝播の使用は自動的に処理されます。この場合、現在のメソッドによって別のメソッドが呼び出された場合、そのメソッドには、進行中のトランザクションに参加するオプションがあります。
例を見てみましょう:
2つのモデルのie Country
とがありCity
ます。リレーショナルマッピングCountry
とCity
1は次のようにモデルがあるCountry
マッピングが似ているように、複数の都市を持つことができ、
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
ここでは、Countryが複数の都市にマッピングされ、それらがフェッチされますLazily
。したがって@Transactinal
、データベースからCountryオブジェクトを取得するときに、国オブジェクトのすべてのデータを取得しますが、都市を取得しているため、都市のセットは取得しませんLAZILY
。
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
国オブジェクトから都市のセットにアクセスする場合、そのセットでnull値を取得します。これは、このセットのみが作成されたセットのオブジェクトは@Transactional
、使用するセットの値を取得するためのデータで初期化されないためです。
//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
、サービスはエンドポイントとの接続を閉じることなく、単一のトランザクションで複数の呼び出しを行うことができます。
@Transactional
本当は何であるかの説明
@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ドキュメント:https : //docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
サービスレイヤーは@Transactional
、ここに存在するほとんどのビジネスロジックとして注釈を追加するのに最適な場所であり、詳細レベルのユースケース動作が含まれています。
それをDAOに追加し、サービスから2つのDAOクラスを呼び出しているとします。1つは失敗し、もう1つは成功してい@Transactional
ます。
したがって、この推奨事項を賢く使用し、サービスレイヤーでのみ使用することをお勧めします。
まず、トランザクションを使用する必要がある場所を定義しましょう。
私は正しい答えだと思います-アクションのシーケンスが1つのアトミック操作として一緒に完了するか、アクションの1つが失敗しても変更が行われないことを確認する必要がある場合。
ビジネスロジックをサービスに組み込むことはよく知られています。そのため、サービスメソッドには、単一の論理的な作業単位として実行する必要があるさまざまなアクションが含まれる場合があります。その場合、そのようなメソッドはトランザクションとしてマークする必要があります。もちろん、すべてのメソッドがこのような制限を必要とするわけではないので、サービス全体をトランザクションとしてマークする必要はありません。
そしてさらに- @Transactionalを考慮することを忘れないでください明らかにメソッドのパフォーマンスを低下させる可能性が。全体像を見るには、トランザクションの分離レベルを知る必要があります。それが必ずしも必要ではない場所で@Transactionalを使用することを避けるのに役立つかもしれないことを知ること。
@Transactionalは、DAOとサービスレイヤーの間の別の中間レイヤーに保持することをお勧めします。ロールバックは非常に重要であるため、すべてのDB操作を中間層に配置し、ビジネスロジックをサービス層に書き込むことができます。中間層はDAO層と相互作用します。
これは、ObjectOptimisticLockingFailureException-この例外はトランザクションが終了した後にのみ発生するなど、多くの状況で役立ちます。したがって、中間層ではキャッチできませんが、サービス層でキャッチできます。サービスレイヤーに@Transactionalがある場合、これは不可能です。Controllerでキャッチすることはできますが、Controllerはできるだけクリーンである必要があります。
保存、削除、更新のすべてのオプションを完了した後、別のスレッドでメールまたはSMSを送信する場合は、中間層でトランザクションが完了した後にサービスでこれを実行できます。繰り返しになりますが、サービス層で@Transactionalについて言及すると、トランザクションが失敗した場合でもメールが送信されます。
したがって、中間の@Transactionレイヤーがあると、コードがより簡単に処理できるようになります。それ以外の場合、DAOレイヤーで使用すると、すべての操作をロールバックできない場合があります。サービス層で使用する場合、場合によってはAOP(アスペクト指向プログラミング)を使用する必要があります。