HackToTech

Hack To Technology

SpringでRollbackされた時に別トランザクションでデータの保存をしようとしてたのでメモ

大まかなコードのイメージ

package com.example.app.application

import com.example.app.domain.Sample
import com.example.app.domain.SampleRepository
import org.springframework.aop.framework.AopContext
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.interceptor.TransactionAspectSupport

@Service
class SampleService(
    private val otherService: OtherService,
    private val fallbackService: FallbackService,
) {
    @Transactional
    fun handle() {
        try {
            // ここの呼び出しがロールバックするなら処理を保存しときたい
           // 実際は他にもトランザクションを使ったなんらかの処理
            otherService.handle()
        } catch (e: RuntimeException) {
            // UnexpectedRollbackExceptionにならないように明示的に指定する必要がある
            val transactionStatus = TransactionAspectSupport.currentTransactionStatus()
            transactionStatus.setRollbackOnly()
            fallbackService.handle()
        }
    }
}

@Service
class OtherService {
    @Transactional
    fun handle() {
        // 実際はなんらかの処理
        throw RuntimeException("runtime error")
    }
}

@Service
class FallbackService(
    private val repository: SampleRepository,
) {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    fun handle() {
        // 実際はデータ受け取って書き込むだけ
        val sample = Sample.of()
        repository.save(sample)
    }
}

なんかこの為だけに別クラス作って呼び出したり、EventPublisher経由で別メソッドを呼び出したりするのがイマイチだなあと思ってたらAOPで解決する方法もあるとのことだった
www.youtube.com 詳解Springトランザクション -初級から上級まで- #jsug | ドクセル


別メソッドで Propagation.REQUIRES_NEW しているなら、トランザクション別で自動的に貼り直してくれれば良いのに
と思っていたらAOPの仕組み的に SampleService#handle に入る際にProxy経由で呼び出されて、
SampleService#handle 内で 別メソッドを呼び出しても割り込まれずに直接呼び出される(Annotationが効かない)という話っぽい
この辺ちゃんと頭の中でイメージ出来ていないと、なんで書いた通りに呼び出されないんだ???となるのでとても良い資料だった

解説されていたサイトの方、どうやってProxy経由で呼び出しているのかちょっとだけ見てみた

あとは SampleService#handle から SampleService(Proxy経由)#別メソッド を呼びだしている、ということっぽい