尽管存在`@Transactional`,为啥这些数据库修改不回滚?
Posted
技术标签:
【中文标题】尽管存在`@Transactional`,为啥这些数据库修改不回滚?【英文标题】:Why aren't these database modifications rolled back despite the presence of an `@Transactional`?尽管存在`@Transactional`,为什么这些数据库修改不回滚? 【发布时间】:2019-09-02 05:17:24 【问题描述】:为Testcontainers
写了一个简短的便利扩展:
fun JdbcDatabaseContainer<*>.execute(query:DSLContext.()-> Query)
val connection = DriverManager.getConnection(this.getJdbcUrl(),this.getUsername(),this.getPassword())
val create = DSL.using(connection)
create.query().execute()
现在想测试一下。
Flyway 加载 30 个条目。这些应该在allDataPresent
中可见
canInsert
插入一个不带扩展名的条目
canInsertWithExtension
做同样的事情,但通过扩展功能
insertMultipleWithExtension
就像它的名字所暗示的那样,插入另一个 5
除了allDataPresent
测试用例(因为它是只读的)之外的所有测试用例都被注释@Transactional
。
因此,我希望在测试方法之后回滚这些修改。
反而会发生
[ERROR] Failures:
[ERROR] InitDataIT.allDataPresent:70
Expecting:
<36>
to be equal to:
<30>
but was not.
[ERROR] InitDataIT.canInsert:90
Expecting:
<6>
to be equal to:
<1>
but was not.
[ERROR] InitDataIT.canInsertWithExtension:112
Expecting:
<6>
to be equal to:
<1>
but was not.
每个@Test
都可以自己正常工作。所以问题一定出在@Transactional
。
那是为什么呢?更重要的是,我如何获得回滚?
完整的测试用例(也尝试注释类,没有任何区别):
@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = [InitDataIT.TestContextInitializer::class])
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
open class InitDataIT
companion object
@JvmStatic
@Container
private val dbContainer = mysqlContainer<Nothing>().apply
withDatabaseName("test")
withUsername("root")
withPassword("")
object TestContextInitializer: ApplicationContextInitializer<ConfigurableApplicationContext>
override fun initialize(applicationContext: ConfigurableApplicationContext)
TestPropertyValues.of(
"spring.datasource.url=$dbContainer.jdbcUrl",
"spring.datasource.username=$dbContainer.username",
"spring.datasource.password=$dbContainer.password",
"spring.datasource.driver-class-name=$dbContainer.driverClassName"
).applyTo(applicationContext)
private val create:DSLContext
@Autowired
constructor(create:DSLContext)
this.create = create
@Test
fun allDataPresent()
//given
val expectedNumberOfEntries = 30
val query = create.selectCount()
.from(CUSTOMERS)
//when
val numberOfEntries = query.fetchOneit.value1()
//then
Assertions.assertThat(numberOfEntries).isEqualTo(expectedNumberOfEntries)
@Test
@Transactional
open fun canInsert()
//given
val insertquery = create.insertInto(CUSTOMERS)
.columns(CUSTOMERS.FIRSTNAME,CUSTOMERS.LASTNAME,CUSTOMERS.EMAIL, CUSTOMERS.STATUS)
.values("Alice","Tester","Alice.Tester@somewhere.tt",CustomerStatus.Contacted.name)
val expectedNumberInOffice2 = 1
//when
insertquery.execute()
//then
val numberInOffice2 = create.selectCount()
.from(CUSTOMERS)
.where(CUSTOMERS.EMAIL.contains("somewhere"))
.fetchOneit.value1()
assertThat(numberInOffice2).isEqualTo(expectedNumberInOffice2)
@Test
@Transactional
open fun canInsertWithExtension()
//given
dbContainer.execute
insertInto(CUSTOMERS)
.columns(CUSTOMERS.FIRSTNAME,CUSTOMERS.LASTNAME,CUSTOMERS.EMAIL, CUSTOMERS.STATUS)
.values("Alice","Tester","Alice.Tester@somewhere.tt",CustomerStatus.Contacted.name)
val expectedNumberInOffice2 = 1
//when
val numberInOffice2 = create.selectCount()
.from(CUSTOMERS)
.where(CUSTOMERS.EMAIL.contains("somewhere"))
.fetchOneit.value1()
//then
assertThat(numberInOffice2).isEqualTo(expectedNumberInOffice2)
@Test
@Transactional
open fun insertMultipleWithExtension()
//given
dbContainer.execute
insertInto(CUSTOMERS)
.columns(CUSTOMERS.FIRSTNAME,CUSTOMERS.LASTNAME,CUSTOMERS.EMAIL, CUSTOMERS.STATUS)
.values("Alice","Make","Alice.Make@somewhere.tt", CustomerStatus.Customer.name)
.values("Bob","Another","Bob.Another@somewhere.tt", CustomerStatus.ClosedLost.name)
.values("Charlie","Integration","Charlie.Integration@somewhere.tt",CustomerStatus.NotContacted.name)
.values("Denise","Test","Denise.Test@somewhere.tt",CustomerStatus.Customer.name)
.values("Ellie","Now","Ellie.Now@somewhere.tt",CustomerStatus.Contacted.name)
val expectedNumberInOffice2 = 5
//when
val numberInOffice2 = create.selectCount()
.from(CUSTOMERS)
.where(CUSTOMERS.EMAIL.contains("somewhere"))
.fetchOneit.value1()
//then
assertThat(numberInOffice2).isEqualTo(expectedNumberInOffice2)
【问题讨论】:
【参考方案1】:Spring @Transactional
注释不仅可以神奇地与您的 DriverManager
创建的 JDBC 连接一起使用。你的 dbContainer
对象应该在你的 spring 管理的数据源上运行。
【讨论】:
嗯,这很有道理。因此,如果我为我的扩展函数创建一个辅助对象,将 Spring 维护的 DSLContext 注入其中,并使用该对象而不是从新的 jdbc 连接创建我自己的,它应该可以工作吗?谢谢,过几个小时试试。以上是关于尽管存在`@Transactional`,为啥这些数据库修改不回滚?的主要内容,如果未能解决你的问题,请参考以下文章
为啥没有@Transactional 我可以保存? [复制]
为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用
为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用