Spring不回退Kotlin未检查异常的事务

时间:2019-11-30 21:50:08

标签: spring kotlin spring-transactions

据我所知,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))
    }

}

1 个答案:

答案 0 :(得分:0)

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

  

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

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

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