😊
繰り返し処理+try構文内では@Transactionalでロールバックできない
概要
springbootでは@Transactionalでトランザクション管理するが、特定の条件ではロールバック出来ない
環境
- springboot2.7.5
- kotlin1.6.21
例
下記の様にサービスクラスのloop関数で繰り返し処理を行い、その繰り返し処理内でtrycatch文で発生したExceptionをcatchする場合、Exceptionが投げられてもinsert関数で作成したレコードがロールバックされず不正なデータが残ってしまう。原因はおそらくSampleUserServiceに設定した@TransactionalがExceptionが投げられた事を判断する前にcatch文でExceptionを握りつぶしてしまっているため
コントローラクラス
@RestController
@RequestMapping("/api/sample")
class SampleApiController(
val sampleUserService: SampleUserService
) {
@GetMapping("/test")
fun test() {
sampleUserService.loop()
}
サービスクラス
@Service
@Transactional(rollbackForClassName = ["Exception"])
class SampleUserService(
private val sampleUserRepository: SampleUserRepository
) {
fun loop() {
val list = listOf("1", "2", "3")
list.forEach {
try {
//try構文内でinsert関数を実行
insert(it)
} catch (e: Exception) {
println("キャッチ")
}
}
}
fun insert(str: String) {
val newUser = User(
userId = str
)
//レコードの挿入
sampleUserRepository.insert(newUser)
//Exceptionを投げる
throw Exception("")
}
}
解決方法
insert関数を別のサービスクラスSampleUserService2に定義してloop関数内で呼び出すとロールバックが成功する。SampleUserService2内で定義された@Transactionalではexceptionがキャッチできるため
コントローラクラス
@RestController
@RequestMapping("/api/sample")
class SampleApiController(
val sampleUserService: SampleUserService
) {
@GetMapping("/test")
fun test() {
sampleUserService.loop()
}
サービスクラス1
@Service
@Transactional(rollbackForClassName = ["Exception"])
class SampleUserService(
private val sampleUserService2: SampleUserService2
) {
fun loop() {
val list = listOf("1", "2", "3")
list.forEach {
try {
//try構文内で別サービスに定義したinsert関数を実行
sampleUserService2.insert(it)
} catch (e: Exception) {
println("キャッチ")
}
}
}
}
サービスクラス2
@Service
//トランザクションは新規に作成する様にオプションを追加
@Transactional(propagation = Propagation.REQUIRES_NEW)
class SampleUserService2(
private val sampleUserRepository: SampleUserRepository
) {
fun insert(str: String) {
val newUser = User(
userId = str
)
//レコードの挿入
sampleUserRepository.insert(newUser)
//Exceptionを投げる
throw Exception("")
}
}
Discussion