Spring:トランザクションサポートによるTaskExecutorの実装

開発者の面倒を見るSpringは、トランザクションマネージャとやり取りするための便利でシンプルな外観を提供します。 ただし、洗練されたアーキテクチャのアイデアを実装するには、標準のメカニズムで常に十分でしょうか? 明らかにそうではありません。



この投稿では、Springの機能について説明します-





@Transactionalアノテーションを使用する最も一般的に使用されるトランザクション仕様。 このメカニズムを使用するには、優先TransactionManagerを構成し、注釈処理を有効にするだけで十分です。 XMLファイルを使用した構成の場合、次のようになります。

..... <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="dataSource" ref="dataSource" /> <property name="entityManagerFactory" ref="entityManagerFactory" /> <property name="jpaDialect" ref="jpaDialect"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> ....
      
      







さらに使用するのは、インターフェイスメソッドの実装または実装クラス全体に@Transactionalアノテーションを掛けるだけです。

 @Service public class FooServiceImpl implements FooService { @Autowired private FooDao fooDao; @Transactional @Override public void update(Foo entity) { fooDao.merge(entity); } }
      
      





ただし、このメカニズムを効果的に使用するには、必ずしも明らかではないいくつかの微妙な点を覚えておく必要があります。



このようなメカニズムにより、トランザクション管理コードを毎回書く必要がなくなります!



ただし、このメカニズムでは不十分な場合がありますか? または注釈を省くことができない状況がありますか? 答えは明らかだと思います。 たとえば、1つのトランザクションでコードの一部を実行し、2番目のトランザクションで別の部分を実行する場合があります(ここでは、メソッドをアーキテクチャ的に正しい2つに分割する可能性が高くなります)。 読者は自分の例があると思う。



より現実的な例は、コードの一部を非同期に実行する必要がある場合です。

 @Service public class FooServiceImpl implements FooService { @Autowired private TaskExecutor taskExecutor; @Autowired private FooDao fooDao; @Transactional @Override public void update(Foo entity) { fooDao.merge(entity); taskExecutor.run(new Runnable() { public void run() { someLongTimeOperation(entity); } }); } @Transactional @Override public void someLongTimeOperation(Foo entity) { //     } }
      
      







何が起こるか:update()メソッドが開始する前に、トランザクションが作成され、その後、本体からの操作が実行され、メソッドを終了すると、トランザクションが閉じられます。 ただし、この場合、コードが実行される新しいスレッドが作成されます。 そして、update()メソッドの終了時および関連するトランザクションの破棄時に、2番目に実行中のスレッドでのコードの実行が継続できることは明らかです。 その結果、メソッドの完了時に、2番目のスレッドで例外が発生し、トランザクション全体が「バックアップ」されます。



前の例に、手動のトランザクション作成を追加します。

 @Service public class FooServiceImpl implements FooService { @Autowired private TaskExecutor taskExecutor; @Autowired private FooDao fooDao; @Transactional @Override public void update(final Foo entity) { fooDao.merge(entity); final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); taskExecutor.execute(new Runnable() { @Override public void run() { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { someLongTimeOperation(entity); } }); } }); } @Transactional @Override public void someLongTimeOperation(Foo entity) { //     } }
      
      







現在、someLongTimeOperation()は、選択したトランザクションで非同期に実行されます。 ただし、面倒な手動制御コードを複製しないように、一般化された実装が必要です。



さて...ここで彼女は:

 public interface TransactionalAsyncTaskExecutor extends AsyncTaskExecutor { void execute(Runnable task, Integer propagation, Integer isolationLevel); }
      
      





 public class DelegatedTransactionalAsyncTaskExecutor implements InitializingBean, TransactionalAsyncTaskExecutor { private PlatformTransactionManager transactionManager; private AsyncTaskExecutor delegate; private TransactionTemplate sharedTransactionTemplate; public DelegatedTransactionalAsyncTaskExecutor() { } public DelegatedTransactionalAsyncTaskExecutor(PlatformTransactionManager transactionManager, AsyncTaskExecutor delegate) { this.transactionManager = transactionManager; this.delegate = delegate; } @Override public void execute(final Runnable task, Integer propagation, Integer isolationLevel) { final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.setPropagationBehavior(propagation); transactionTemplate.setIsolationLevel(isolationLevel); delegate.execute(new Runnable() { @Override public void run() { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { task.run(); } }); } }); } @Override public void execute(final Runnable task) { execute(task, TransactionDefinition.PROPAGATION_REQUIRED, TransactionDefinition.ISOLATION_DEFAULT); } @Override public void execute(final Runnable task, long startTimeout) { final TransactionTemplate transactionTemplate = getSharedTransactionTemplate(); delegate.execute(new Runnable() { @Override public void run() { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { task.run(); } }); } }, startTimeout); } @Override public Future<?> submit(final Runnable task) { final TransactionTemplate transactionTemplate = getSharedTransactionTemplate(); return delegate.submit(new Runnable() { @Override public void run() { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { task.run(); } }); } }); } @Override public <T> Future<T> submit(final Callable<T> task) { final TransactionTemplate transactionTemplate = getSharedTransactionTemplate(); return delegate.submit(new Callable<T>() { @Override public T call() throws Exception { return transactionTemplate.execute(new TransactionCallback<T>() { @Override public T doInTransaction(TransactionStatus status) { T result = null; try { result = task.call(); } catch (Exception e) { e.printStackTrace(); status.setRollbackOnly(); } return result; } }); } }); } public PlatformTransactionManager getTransactionManager() { return transactionManager; } public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } public AsyncTaskExecutor getDelegate() { return delegate; } public void setDelegate(AsyncTaskExecutor delegate) { this.delegate = delegate; } public TransactionTemplate getSharedTransactionTemplate() { return sharedTransactionTemplate; } public void setSharedTransactionTemplate(TransactionTemplate sharedTransactionTemplate) { this.sharedTransactionTemplate = sharedTransactionTemplate; } @Override public void afterPropertiesSet() { if (transactionManager == null) { throw new IllegalArgumentException("Property 'transactionManager' is required"); } if (delegate == null) { delegate = new SimpleAsyncTaskExecutor(); } if (sharedTransactionTemplate == null) { sharedTransactionTemplate = new TransactionTemplate(transactionManager); } } }
      
      







この実装はラッパーであり、その結果、SpringにいくつかあるTaskExecutorへの呼び出しを遅延させます。 さらに、各呼び出しはトランザクションで「ラップ」されます。 TransactionTemplateを使用してSpringでトランザクションを手動で管理できますが、EntityManager#getTransaction()は例外をスローします。



そして最後に、実際の実用例について:



TaskExecutorを構成します。

  <bean id="transactionalTaskExecutor" class="ru.habrahabr.support.spring.DelegatedTransactionalAsyncTaskExecutor"> <property name="delegate"> <bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="threadNamePrefix" value="Habrahabr example - "/> <property name="threadGroupName" value="Habrahabr examples Group"/> <property name="corePoolSize" value="10"/> <property name="waitForTasksToCompleteOnShutdown" value="true"/> </bean> </property> <property name="transactionManager" ref="transactionManager"/> </bean>
      
      







サービス例:



 @Service public class FooServiceImpl implements FooService { @Autowired private TransactionalAsyncTaskExecutor trTaskExecutor; @Autowired private FooDao fooDao; @Transactional @Override public void update(Foo entity) { fooDao.merge(entity); //     Spring' (tr_1). trTaskExecutor.run(new Runnable() { //       (tr_2),  run()         tr_2. public void run() { someLongTimeOperation(); } }); } //        tr_1 .  tr_2    TransactionTemplate. @Transactional @Override public void someLongTimeOperation(Foo entity) { //     } }
      
      







したがって、TaskExecutorの完全に一般化されたラッパー実装があり、トランザクション作成コードの重複を回避できます。



All Articles