コマンドパターンを使用して、1つのトランザクションのコンテキスト内でWebレイヤーがHibernateエンティティを操作できるようにする実験を行っています(したがって、遅延読み込みの例外を回避します)。しかし、私は今、トランザクションをどのように扱うべきかと混同しています。
私のコマンドは、_@Transactional
_アノテーションが付けられたサービスレイヤーメソッドを呼び出します。これらのサービスレイヤーメソッドの一部は読み取り専用です。 @Transactional(readOnly=true)
-一部は読み取り/書き込み可能です。
私のサービスレイヤーは、Webレイヤーの代わりに渡されたコマンドを実行するコマンドハンドラーを公開します。
_@Transactional
public Command handle( Command cmd ) throws CommandException
_
私は、コマンドハンドラーのhandle()
メソッドをトランザクション対応にするのが正しいと思います。これが混乱の原因です。コマンドの実装が複数のサービスレイヤーメソッドを呼び出す場合、コマンドハンドラーは、コマンド内で呼び出された操作が読み取り専用、読み取り/書き込み、または組み合わせかどうかを知る方法がありません。 2の。
この例では、伝播の仕組みがわかりません。 handle()
メソッド_readOnly=true
_を作成した場合、コマンドが@Transactional(realOnly=false)
アノテーションが付けられたサービスレイヤーメソッドを呼び出すとどうなりますか?
これについての理解を深めていただき、コメントをお待ちしております...
アンドリュー
まず第一に、Springは永続化を行わないため、readOnly
が正確に意味するものを指定できません。この属性はプロバイダーへの単なるヒントであり、動作はこの場合はHibernateに依存します。
readOnly
としてtrue
を指定すると、現在のHibernateセッションでフラッシュモードがFlushMode.NEVER
に設定され、セッションがトランザクションをコミットできなくなります。
さらに、JDBC接続でsetReadOnly(true)が呼び出されます。これは、基になるデータベースへのヒントでもあります。データベースがサポートしている場合(ほとんどの場合サポートしています)、これは基本的にFlushMode.NEVER
と同じ効果がありますが、手動でフラッシュすることさえできないため、より強力です。
次に、トランザクションの伝播がどのように機能するかを見てみましょう。
readOnly
をtrue
に明示的に設定しない場合、読み取り/書き込みトランザクションが発生します。トランザクション属性(REQUIRES_NEW
など)に応じて、ある時点でトランザクションが中断され、新しいトランザクションが開始されて最終的にコミットされ、その後、最初のトランザクションが再開されます。
OK、もうすぐです。 readOnly
がこのシナリオにもたらすものを見てみましょう。
read/writeトランザクション内のメソッドがreadOnlyトランザクションを必要とするメソッドを呼び出す場合、最初のメソッドは中断する必要があります。そうしないと、最後にフラッシュ/コミットが発生するためです2番目の方法の。
逆に、readOnlyを必要とするreadOnlyトランザクション内からメソッドを呼び出すと、再度フラッシュ/コミットできないため、最初のトランザクションが中断されます。 2番目の方法ではそれが必要です。
readOnly-to-readOnlyおよびread/write-to-read/writeの場合、外側のトランザクションを中断する必要はありません(特に伝播を指定しない限り) 、明らかに)。
前のトランザクションが継続するため、readOnly = trueからreadOnly = falseを呼び出しても機能しません。
この例では、サービスレイヤーのhandle()メソッドが新しい読み取り/書き込みトランザクションを開始しています。ハンドルメソッドが読み取り専用の注釈を付けたサービスメソッドを呼び出す場合、読み取り専用は既存の読み取り/書き込みトランザクションに参加するため、効果はありません。
これらのメソッドが読み取り専用であることが不可欠な場合は、Propagation.REQUIRES_NEWでアノテーションを付けることができ、既存の読み取り/書き込みトランザクションに参加するのではなく、新しい読み取り専用トランザクションを開始します。
これが実際の例です。CircuitStateRepositoryは、スプリングデータJPAリポジトリです。
BeanSは、transactional = read-only Bean1を呼び出します。これは、ルックアップを実行し、新しいオブジェクトを保存するtransactional = read-write Bean2を呼び出します。
31 09:39:44.199 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager-[nz.co.vodafone.wcim.business.Bean1.startSomething]:PROPAGATION_REQUIRED、ISOLATION_DEFAULT、readOnlyという名前の新しいトランザクションを作成します。 ''
Bean 2はそれに参加します。
31 09:39:44.230 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager-既存のトランザクションへの参加
データベースには何もコミットされません。
Bean2を変更します@Transactional
追加する注釈propagation=Propagation.REQUIRES_NEW
Bean1は読み取り専用のtxを開始します。
31 09:31:36.418 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager-[nz.co.vodafone.wcim.business.Bean1.startSomething]:PROPAGATION_REQUIRED、ISOLATION_DEFAULT、readOnlyという名前の新しいトランザクションを作成します。 ''
Bean2は新しい読み取り/書き込みtxを開始します
31 09:31:36.449 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager-現在のトランザクションを中断し、[nz.co.vodafone.wcim.business.Bean2.createSomething]という名前の新しいトランザクションを作成します
また、Bean2による変更はデータベースにコミットされます。
以下は、spring-data、hibernate、およびOracleでテストした例です。
@Named
public class BeanS {
@Inject
Bean1 bean1;
@Scheduled(fixedRate = 20000)
public void runSomething() {
bean1.startSomething();
}
}
@Named
@Transactional(readOnly = true)
public class Bean1 {
Logger log = LoggerFactory.getLogger(Bean1.class);
@Inject
private CircuitStateRepository csr;
@Inject
private Bean2 bean2;
public void startSomething() {
Iterable<CircuitState> s = csr.findAll();
CircuitState c = s.iterator().next();
log.info("GOT CIRCUIT {}", c.getCircuitId());
bean2.createSomething(c.getCircuitId());
}
}
@Named
@Transactional(readOnly = false)
public class Bean2 {
@Inject
CircuitStateRepository csr;
public void createSomething(String circuitId) {
CircuitState c = new CircuitState(circuitId + "-New-" + new DateTime().toString("hhmmss"), new DateTime());
csr.save(c);
}
}
デフォルトでは、トランザクションの伝播は必須です。つまり、同じトランザクションがトランザクションの呼び出し元からトランザクションの呼び出し先に伝播します。この場合、読み取り専用ステータスも伝播します。例えば。読み取り専用トランザクションが読み取り/書き込みトランザクションを呼び出す場合、トランザクション全体が読み取り専用になります。
ビューでセッションを開くパターンを使用して、遅延読み込みを許可できますか?このように、ハンドルメソッドはトランザクションである必要はまったくありません。
現在のアクティブなトランザクションの設定は無視されるようで、新しいトランザクションにのみ設定を適用します。
org.springframework.transaction.PlatformTransactionManager TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException 指定に従って、現在アクティブなトランザクションを返すか、新しいトランザクションを作成します。 分離レベルやタイムアウトなどのパラメーターは新しいトランザクションにのみ適用されるため、アクティブなトランザクションに参加する場合は無視されることに注意してください。 さらに、すべてのトランザクション定義設定がサポートされるわけではありませんすべてのトランザクションマネージャー:適切なトランザクションマネージャーの実装は、サポートされていない設定に遭遇した場合に例外をスローする必要があります。 上記のルールの例外は読み取り専用フラグです。 。基本的に、読み取り専用フラグは潜在的な最適化の単なるヒントです。