Spring не откат транзакции для непроверенных исключений Kotlin - PullRequest
0 голосов
/ 01 декабря 2019

Как я знаю, весенняя транзакция автоматически откатывается для всех непроверенных исключений. Добавив к тому факту, что у 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 голосов
/ 01 декабря 2019

Хотя у Kotlin нет проверенных исключений , у него все еще есть класс RuntimeException . Начиная с Spring, вы можете прочитать в документации об управлении транзакциями, что:

В конфигурации по умолчанию код инфраструктуры транзакций Spring Framework помечает транзакцию для отката только в случаевремя выполнения, непроверенные исключения;то есть, когда выброшенное исключение является экземпляром или подклассом RuntimeException.

Зная, что мы можем попытаться изменить базовый класс нашего исключения с Exception на RuntimeException, чтобы увидеть, что тогда все работаеткак и ожидалось.

Возможно, это не то, что мы ожидаем, но мы должны помнить, что после компиляции разница между обоими типами исключений сводится не более чем к иерархии классов. Поэтому Spring может только проверить, расширяет ли наш класс RuntimeException или нет. Теоретически это может откатить транзакцию для всех типов исключений, но это имеет свои недостатки. Во-первых, поскольку Spring предоставляет только одну версию двоичных файлов для Java и Kotlin, он должен будет решить, поддерживать ли откат для подклассов RuntimeException (более распространенный в Java) или для подклассов Exception (более распространенный в Kotlin). Кроме того, он автоматически откатит транзакцию в случае любого исключения, даже если мы перехватим ее и обработаем сбой внутри транзакции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...