@Transactionalアノテーションはどこに属していますか?


528

あなたは配置する必要があります@TransactionalDAO、クラスおよび/またはそのメソッドまたはそれはDAOオブジェクトを使用して呼び出しているサービスクラスに注釈を付けること良いですか?または、両方の「レイヤー」に注釈を付けることは理にかなっていますか?

回答:


537

トランザクションはサービス層に属していると思います。作業単位とユースケースについて知っているものです。単一のトランザクションで連携する必要がある複数のDAOがサービスに挿入されている場合は、これが正しい答えです。


私はそれに同意します。場合によっては問題にならないこともありますが、Hibernateセッションがwhileトランザクションにまたがっているなどのメリットがある場合があります。そのため、読み込まれたすべてのオブジェクトは1次キャッシュにあり、オブジェクトをセッションに再接続する必要がなく、遅延して読み込まれます。プロパティはファズなしで機能します。
dma_k

5
グローバルトランザクションは、これらの@Transactional(propagation = Propagation.REQUIRED)の複数で構成できますか?または、@ Transactionalは常にトランザクションの境界ですか?ドキュメントからそれを取得したかどうかは
わかりませんが、@ Transactional

1
はい、Propagation.REQUIREDがオンになっている場合、最も外側の@Transactionalがトランザクションの境界になるものだと思います。
duffymo 2010年


309

一般に、トランザクションは通常サービスレベルで開始される(もちろん、必要な粒度に応じて)と述べている他の人にも同意します。

ただし、その間に@Transactional(propagation = Propagation.MANDATORY)、DAOレイヤー(およびトランザクションを開始することは許可されていないが、既存のレイヤーを必要とする他のレイヤー)への追加も開始しました。例:サービス)。DAOに必須の伝播の注釈が付けられている場合、メソッドが呼び出されたときにアクティブなトランザクションがないことを示す例外が発生します。

また、このアノテーションについてすべてのBean(Beanポストプロセッサ)をチェック@Transactionalし、サービスレイヤーに属していないBeanに必須以外の伝播を伴うアノテーションがあると失敗する統合テストもあります。このようにして、間違ったレイヤーでトランザクションを開始しないようにします。


3
これをdao(インターフェース)レイヤー、dao実装で行う必要があります。レイヤーまたは両方?
ヨハン

3
この説明に当てはまるかどうかはわかりませんが、書き込み以外の操作のdao実装に@Transactional(readOnly = true)を追加することもできます。
maxivis 2013年

10
@Johan Springは、インターフェースではなく実装クラスにTransactionアノテーションを付けることをお勧めします。
Mathias G.

私はこのアイデアが本当に好きで、自分のプロジェクトでもそれを試しています
Thomas Einwaller 14

1
Lemme理解できるかどうか確認してください... @TransactionalService実装クラスに入れるべき@Transactional(propagation = MANDATORY)で、DAO(リポジトリ)クラス実装に入れるべきですか?
John Henckel 2017年

93

トランザクションアノテーションは、分離できないすべての操作の周囲に配置する必要があります。

たとえば、あなたの呼び出しは「パスワードの変更」です。それは2つの操作で構成されています

  1. パスワードを変更します。
  2. 変更を監査します。
  3. パスワードが変更されたことをクライアントにメールで送信します。

上記のように、監査が失敗した場合、パスワードの変更も失敗する必要がありますか?その場合、トランザクションは約1と2になるはずです(サービス層で)。電子メールが失敗した場合(おそらくこれに何らかのフェイルセーフが必要であるため、失敗しないでしょう)、パスワードの変更と監査をロールバックする必要がありますか?

これらは、をどこに置くかを決めるときに尋ねる必要がある種類の質問です@Transactional


41

従来のSpringアーキテクチャーの正解は、他の人がすでに説明している理由から、トランザクションセマンティクスをサービスクラスに配置することです。

Springの新しいトレンドは、ドメイン駆動設計(DDD)です。Spring Rooはその傾向をよく示しています。アイデアは、ドメインオブジェクトのPOJOを通常のSpringアーキテクチャ(通常は貧弱なアーキテクチャ)よりもはるかに豊かにすること、特にドメインオブジェクト自体にトランザクションと永続性のセマンティクスを配置することです。必要なのが単純なCRUD操作だけの場合、WebコントローラーはドメインオブジェクトのPOJO(このコンテキストではエンティティとして機能します)を直接操作し、サービス層はありません。ドメインオブジェクト間で何らかの調整が必要な場合は、サービスBeanで次のように処理できます。@Transaction伝統に従って。ドメインオブジェクトのトランザクションの伝播を次のようにREQUIRED設定して、ドメインオブジェクトがサービスBeanで開始されたトランザクションなどの既存のトランザクションを使用するようにすることができます。

技術的には、この手法ではAspectJとを使用します<context:spring-configured />。Rooは、AspectJのタイプ間定義を使用して、エンティティのセマンティクス(トランザクションと永続性)をドメインオブジェクト(基本的にフィールドとビジネスメソッド)から分離します。


38

通常のケースはサービス層レベルで注釈を付けることですが、これは実際には要件に依存します。

サービス層に注釈を付けると、DAOレベルに注釈を付けるよりもトランザクションが長くなります。たとえば、並行トランザクションはお互いの変更を認識しないため、問題を解決できるトランザクション分離レベルに依存します。反復可能な読み取り。

DAOに注釈を付けると、トランザクションができるだけ短くなりますが、サービス層が公開している機能が単一の(ロールバック可能な)トランザクションで実行されないという欠点があります。

伝播モードがデフォルトに設定されている場合、両方のレイヤーに注釈を付けることは意味がありません。


31

レイヤーに配置し@Transactionalて例外を@Service設定し、トランザクションをさらに最適化します。rollbackForreadOnly

デフォルトで@Transactionalは、RuntimeException(チェックされていない例外)のみを検索します。ロールバックをException.class(チェックされた例外)に設定することで、すべての例外をロールバックします。

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

チェック済み例外とチェックなし例外を参照してください。


17

または、両方の「レイヤー」に注釈を付けることは理にかなっていますか?-サービスレイヤーとdaoレイヤーの両方に注釈を付けるのは意味がありません-DAOで伝播が「必須」のサービスレイヤーからDAOメソッドが常に呼び出される(伝播される)ことを確認したい場合。これは、UIレイヤー(またはコントローラー)からDAOメソッドが呼び出されないようにするためのいくつかの制限を提供します。また、特にDAO層をユニットテストする場合、DAOに注釈を付けると、トランザクション機能についてもテストされます。


これにより、入れ子になったトランザクションが発生しませんか?そしてそれに伴うすべての微妙な問題?
HDave

11
いいえ、JPAにはネストされたトランザクションはありません。両方に配置しても問題はありません。DAOにアクセスしたときに既にトランザクションに参加している場合、そのトランザクションは続行されます。
fool4jesus

4
を使用すると、ネストされたトランザクションが発生する可能性がありますpropagation=Propagation.REQUIRES_NEW。それ以外の場合、propogation = mandatoryを含むほとんどの場合、DAOはサービス層によって開始された既存のトランザクションに参加します。
ダンカーター2013年


15

データベースレベルのトランザクションの場合

ほとんど私@TransactionalはDAOのメソッドレベルでのみ使用しているため、構成は特にメソッド用/デフォルトを使用することができます(必須)

  1. データフェッチ(選択..)を取得するDAOのメソッド- @Transactional これを必要としない場合 、トランザクションインターセプター/およびAOPプロキシも実行する必要があるため、オーバーヘッドが発生する可能性があります。

  2. 挿入/更新を行うDAOのメソッドは、 @Transactional

超越に関する非常に良いブログ

アプリケーションレベルの場合-
ビジネスロジックにトランザクションを使用していますが、予期しないエラーが発生した場合にロールバックできるようにしたい

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}

6
およそ1非常に素晴らしい記事TransactionalJava
オーク

11

通常、トランザクションはサービス層に置く必要があります。

しかし、前に述べたように、操作の原子性は注釈が必要な場所を教えてくれます。したがって、Hibernateのようなフレームワークを使用する場合、オブジェクトに対する単一の「保存/更新/削除/ ...変更」操作により、(オブジェクトグラフを介してカスケードされるため)複数のテーブルの複数の行が変更される可能性があります。もちろん、この特定のDAOメソッドにはトランザクション管理も必要です。


10

@Transactional注釈は、分離できないすべての操作の周囲に配置する必要があります。@Transactionalトランザクションの伝播の使用は自動的に処理されます。この場合、現在のメソッドによって別のメソッドが呼び出された場合、そのメソッドには、進行中のトランザクションに参加するオプションがあります。

例を見てみましょう:

2つのモデルのie CountryとがありCityます。リレーショナルマッピングCountryCity1は次のようにモデルがある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、サービスはエンドポイントとの接続を閉じることなく、単一のトランザクションで複数の呼び出しを行うことができます。


2
とても参考になります、ありがとう!私が探していたもの、@Transactional本当は何であるかの説明
CybeX

6

サービスレイヤーに配置することをお勧めします。これは、昨日出会った記事の1つで明確に説明されています!こちらがチェックできるリンクです!


5

ここに画像の説明を入力してください

@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


5

サービスレイヤーは@Transactional、ここに存在するほとんどのビジネスロジックとして注釈を追加するのに最適な場所であり、詳細レベルのユースケース動作が含まれています。

それをDAOに追加し、サービスから2つのDAOクラスを呼び出しているとします。1つは失敗し、もう1つは成功してい@Transactionalます。

したがって、この推奨事項を賢く使用し、サービスレイヤーでのみ使用することをお勧めします。

Githubプロジェクト-java-algos


ObjectOptimisticLockingFailureExceptionなどの例外は、トランザクションが完了した後にのみ発生します。メールサービスなどの他の操作用に別のスレッドがある場合、この設計は完全に失敗します。私たちは今、苦しんでいます。残された唯一のソリューションはAOPです。
Nabin Kumar Khatiwada

4

まず、トランザクションを使用する必要がある場所を定義しましょう。

私は正しい答えだと思います-アクションのシーケンスが1つのアトミック操作として一緒に完了するか、アクションの1つが失敗しても変更が行われないことを確認する必要がある場合。

ビジネスロジックをサービスに組み込むことはよく知られています。そのため、サービスメソッドには、単一の論理的な作業単位として実行する必要があるさまざまなアクションが含まれる場合があります。その場合、そのようなメソッドはトランザクションとしてマークする必要があります。もちろん、すべてのメソッドがこのような制限を必要とするわけではないので、サービス全体をトランザクションとしてマークする必要はありません。

そしてさらに- @Transactionalを考慮することを忘れないでください明らかにメソッドのパフォーマンスを低下させる可能性が。全体像を見るには、トランザクションの分離レベルを知る必要があります。それが必ずしも必要ではない場所で@Transactionalを使用することを避けるのに役立つかもしれないことを知ること。


3

@Transactionalは、DAOとサービスレイヤーの間の別の中間レイヤーに保持することをお勧めます。ロールバックは非常に重要であるため、すべてのDB操作を中間層に配置し、ビジネスロジックをサービス層に書き込むことができます。中間層はDAO層と相互作用します。

これは、ObjectOptimisticLockingFailureException-この例外はトランザクションが終了した後にのみ発生するなど、多くの状況で役立ちます。したがって、中間層ではキャッチできませんが、サービス層でキャッチできます。サービスレイヤーに@Transactionalがある場合、これは不可能です。Controllerでキャッチすることはできますが、Controllerはできるだけクリーンである必要があります。

保存、削除、更新のすべてのオプションを完了した後、別のスレッドでメールまたはSMSを送信する場合は、中間層でトランザクションが完了した後にサービスでこれを実行できます。繰り返しになりますが、サービス層で@Transactionalについて言及すると、トランザクションが失敗した場合でもメールが送信されます。

したがって、中間の@Transactionレイヤーがあると、コードがより簡単に処理できるようになります。それ以外の場合、DAOレイヤーで使用すると、すべての操作をロールバックできない場合があります。サービス層で使用する場合、場合によってはAOP(アスペクト指向プログラミング)を使用する必要があります。


2

理想的には、サービスレイヤー(マネージャー)はビジネスロジックを表すため、@Transactional。サービスメソッドにN個のDAO操作がある状況を想定してみましょう。最初のDAO操作が失敗した場合、他のDAO操作がまだ成功している可能性があり、一貫性のないDB状態になります。注釈サービス層は、このような状況からあなたを救うことができます。



1

@Transactionalコントローラ層(@Controller)を使用して呼び出されるサービス層で使用し、DAO層(@Repository)へのサービス層呼び出し、つまりデータベース関連の操作を使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.