@Transactional
をDAO
クラスおよび/またはそのメソッドに配置する必要がありますか?それとも、DAO オブジェクトを使用して呼び出している Service クラスに注釈を付ける方がよいでしょうか? それとも、両方の「レイヤー」に注釈を付けることは理にかなっていますか?
19 に答える
トランザクションはサービス層に属していると思います。それは、作業単位とユースケースについて知っているものです。1 つのトランザクションで連携する必要がある複数の DAO をサービスに挿入する場合は、これが正しい答えです。
一般に、トランザクションは通常サービスレベルで開始されると述べている他の人たちに同意します(もちろん、必要な粒度によって異なります)。
ただし、その間に@Transactional(propagation = Propagation.MANDATORY)
、DAOレイヤー(およびトランザクションの開始を許可されていないが既存のレイヤーを必要とする他のレイヤー)にも追加を開始しました。これは、呼び出し元でトランザクションを開始するのを忘れた場合のエラーを検出するのがはるかに簡単だからです(例:サービス)。DAOに必須の伝播の注釈が付けられている場合、メソッドが呼び出されたときにアクティブなトランザクションがないことを示す例外が発生します。
また、すべてのBean(Beanポストプロセッサ)でこのアノテーションをチェック@Transactional
し、サービスレイヤーに属していないBeanに必須以外の伝播を伴うアノテーションがある場合は失敗する統合テストもあります。このようにして、間違ったレイヤーでトランザクションを開始しないようにします。
トランザクション アノテーションは、切り離せないすべての操作の周りに配置する必要があります。
たとえば、あなたの呼び出しは「パスワードの変更」です。それは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)
チェックされた例外とチェックされていない例外を参照してください。
データベース レベルのトランザクションの場合
ほとんどの場合@Transactional
、DAO のメソッド レベルでのみ使用したため、構成はメソッド専用に行うことができます / デフォルトを使用する (必須)
データ フェッチを取得する DAO のメソッド (select .. ) - これは必要ありません。
@Transactional
トランザクション インターセプター / および AOP プロキシも実行する必要があるため、オーバーヘッドが発生する可能性があります。挿入/更新を行うDAOのメソッドは取得します
@Transactional
トランザクショナルに関する非常に優れたブログ
アプリケーション レベルの場合 -
ビジネス ロジックにトランザクションを使用しています。予期しないエラーが発生した場合にロールバックできるようにしたいと考えています。
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
それとも、両方の「レイヤー」に注釈を付けることは理にかなっていますか? -サービスレイヤーとdaoレイヤーの両方に注釈を付けるのは意味がありません-DAOで「必須」の伝播を使用して、DAOメソッドが常にサービスレイヤーから呼び出される(伝播される)ことを確認したい場合。これにより、DAO メソッドが UI レイヤー (またはコントローラー) から呼び出されるのを制限することができます。また、特に DAO 層を単体テストする場合、DAO に注釈を付けると、トランザクション機能についても確実にテストされます。
また、Spring は、具体的なクラスでのみアノテーションを使用し、インターフェースでは使用しないことをお勧めします。
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
@Transactional
注釈は、切り離せないすべての操作に配置する必要があります。トランザクション伝播の使用@Transactional
は自動的に処理されます。この場合、現在のメソッドによって別のメソッドが呼び出された場合、そのメソッドには進行中のトランザクションに参加するオプションがあります。
それでは、例を見てみましょう:
ieCountry
との 2 つのモデルがありCity
ます。モデルのリレーショナル マッピングはCountry
、複数の都市を持つことができるようなものなので、マッピングは次のようになります。City
Country
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
ここで Country は複数の都市にマッピングされ、それらを取得していますLazily
。したがって、データベースから Country オブジェクトを取得するときの役割がここにあり@Transactinal
ます。次に、Country オブジェクトのすべてのデータを取得しますが、都市を取得しているため、都市のセットは取得しませんLAZILY
。
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
国オブジェクトから Set of Cities にアクセスする場合、その Set で null 値を取得します。これは、この Set のみを作成した Set のオブジェクトが、使用する Set の値を取得するためのデータで初期化されていないためです@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
にサービスは、エンドポイントとの接続を閉じることなく、単一のトランザクションで複数の呼び出しを行うことができます。
通常、トランザクションはサービス層に配置する必要があります。
しかし、前に述べたように、操作のアトミック性によって、アノテーションが必要な場所がわかります。したがって、Hibernate のようなフレームワークを使用すると、オブジェクトに対する単一の「保存/更新/削除/...変更」操作で複数のテーブルの複数の行が変更される可能性があります (オブジェクト グラフを介したカスケードのため)。もちろん、この特定の DAO メソッドにはトランザクション管理も必要です。
It is better to have it in the service layer! This is clearly explained on one of the article that I came across yesterday! Here is the link that you can check out!
理想的には、サービス層 (マネージャー) はビジネス ロジックを表すため、注釈を付ける必要があります。サービス層は@Transactional
、DB 操作を実行するために異なる DAO を呼び出す場合があります。サービスメソッドに N 個の DAO 操作がある状況を想定してみましょう。最初の DAO 操作が失敗した場合でも、他の操作は成功する可能性があり、最終的に DB の状態が一貫しなくなります。サービス層に注釈を付けると、そのような状況からあなたを救うことができます。
@Transactional
コントローラ層 ( ) を使用して呼び出されるサービス層で使用し、サービス層@Controller
から DAO 層 ( @Repository
) を呼び出します。つまり、データベース関連の操作です。
@Transactional
メソッドレベルでサービスレイヤーで使用することを好みます。