【问题标题】:Spring don't rollback transaction for Kotlin unchecked exceptions对于 Kotlin 未经检查的异常,Spring 不会回滚事务
【发布时间】:2019-11-30 21:50:08
【问题描述】:

据我所知,spring 事务会自动回滚所有未经检查的异常。再加上 Kotlin 只有未经检查的异常这一事实,我预计应该为所有这些异常回滚事务。

不幸的是,这不是它的工作方式。下面有一个完整的例子,展示了我正在做的事情。为什么这不起作用?

package example1

import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.data.jpa.repository.Modifying
import org.springframework.data.jpa.repository.Query
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import org.springframework.test.context.junit.jupiter.SpringJUnitJupiterConfig
import org.springframework.transaction.annotation.EnableTransactionManagement
import org.springframework.transaction.annotation.Transactional
import javax.persistence.*

// EXCEPTIONS
class MyCustomException(message: String) : Exception(message)


// ENTITIES

@Entity
@Table(name = "posts")
class Post(
        @Id
        @Column(name = "id")
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        var id: Long? = null,

        @Version
        @Column(name = "version")
        var version: Long? = null,

        @Column(name = "score")
        var score: Int = 0
)


// REPOSITORIES

@Repository
interface PostRepository : PagingAndSortingRepository<Post, Long> {

    @Modifying
    @Query(value = "update Post p set p.score = p.score + 1 where p.id = :postId")
    fun upvote(@Param("postId") postId: Long)

    @Query(value = "select p.score from Post p where p.id = :postId")
    fun getScore(@Param("postId") postId: Long): Int?
}


// SERVICES

interface PostService {
    fun upvote(postId: Long)
}

@Service
open class PostServiceImpl(
        @Autowired
        val postRepository: PostRepository
) : PostService {

    @Transactional//(rollbackFor = [MyCustomException::class])
    override fun upvote(postId: Long) {
        postRepository.upvote(postId)

        throw MyCustomException("Something wrong happend!")
    }

}

// CONFIGURATION

@EnableJpaRepositories(basePackages = ["example1"])
@EnableTransactionManagement
@SpringBootApplication(scanBasePackages = ["example1"])
open class FrameworkApplication


// TESTS

@SpringJUnitJupiterConfig(classes = [FrameworkApplication::class])
@DisplayName("Rollback test")
class TestClass(
        @Autowired
        val postService: PostService,

        @Autowired
        val postRepository: PostRepository
) {

    @AfterEach
    fun cleanUp() {
        postRepository.deleteAll()
    }

    @Test
    @DisplayName("Should rollback after exception")
    fun testUpvote() {
        //given
        var post = Post()
        post = postRepository.save(post)
        val postId = post.id!!

        //then
        Assertions.assertThrows(Exception::class.java) {
            postService.upvote(postId)
        }

        //then
        Assertions.assertEquals(0, postRepository.getScore(postId))
    }

}

【问题讨论】:

    标签: spring kotlin spring-transactions


    【解决方案1】:

    虽然 Kotlin 没有 checked exceptions,但它仍然有 RuntimeException 类。从 Spring 开始,您可以在 documentation 中阅读有关事务管理的内容:

    在其默认配置中,Spring Framework 的事务基础结构代码仅在运行时、未经检查的异常情况下将事务标记为回滚;也就是说,当抛出的异常是 RuntimeException 的实例或子类时。

    知道我们可以尝试将异常的基类从 Exception 更改为 RuntimeException 以确保一切正常。

    这可能不是我们所期望的,但我们必须记住,在编译之后,两种异常类型之间的差异只不过是类层次结构。所以 Spring 只能检查我们的类是否扩展了RuntimeException。理论上它可以为所有异常类型回滚事务,但这有其缺点。首先,由于 Spring 只为 Java 和 Kotlin 提供了一个版本的二进制文件,因此它必须决定是否支持 RuntimeException 的子类(在 Java 中更常见)或 Exception 的子类(在 Kotlin 中更常见)的回滚。此外,它会在任何异常情况下自动回滚事务,即使我们捕获它并处理事务内部的失败。

    【讨论】:

      猜你喜欢
      • 2021-03-23
      • 2021-02-22
      • 2018-12-24
      • 2012-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-21
      • 2022-07-22
      相关资源
      最近更新 更多