Spring 启动自动提交 manuell 事务而不调用存储库保存
Spring Boot auto commit manuell transaction without call save on repository
Spring Boot 尝试自动提交实体 personTransaction1 的 manuell 事务中的更改,而不调用存储库中的保存方法。 personTransaction1 中的更改已提交。方法manuellTransaction抛出org.springframework.orm.ObjectOptimisticLockingFailureException。在方法 transactionByAnnotation 中使用基于注释的事务处理的相同代码按预期工作,并且没有提交变量 personTransaction1 的更改。 spring 尝试提交的原因是什么?
@SpringBootApplication
@EnableJpaAuditing
class DbOptimiticLockingApplication : CommandLineRunner {
@Autowired
private lateinit var personRepository: PersonRepository
@Autowired
private lateinit var platformTransactionManager: PlatformTransactionManager
override fun run(vararg args: String?) {
executeInNewTransaction {
personRepository.deleteAll()
}
val person = createPerson()
transactionByAnnotation(person.id)
manuellTransaction(person.id)
}
@Transactional
fun createPerson(): Person {
val person = Person()
person.name = "Max"
return personRepository.save(person)
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
fun transactionByAnnotation(id: Long) {
var personTransaction1 = personRepository.findById(id).get()
// don't trigger commit
personTransaction1.name = "Tom"
transaction11ByAnnotation(personTransaction1.id)
transaction12ByAnnotation(personTransaction1.id)
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
fun transaction11ByAnnotation(id: Long) {
businessLogic1(id)
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
fun transaction12ByAnnotation(id: Long) {
businessLogic2(id)
}
fun manuellTransaction(id: Long) {
executeInNewTransaction {
var personTransaction1 = personRepository.findById(id).get()
// trigger commit
personTransaction1.name = "Tom"
manuellTransaction11(personTransaction1.id)
manuellTransaction12(personTransaction1.id)
}
}
fun manuellTransaction11(id: Long) {
executeInNewTransaction {
businessLogic1(id)
}
}
fun manuellTransaction12(id: Long) {
executeInNewTransaction {
businessLogic2(id)
}
}
private fun businessLogic1(id: Long) {
val person = personRepository.findById(id).get()
person.name = "Martin"
personRepository.saveAndFlush(person)
}
private fun businessLogic2(id: Long) {
val person = personRepository.findById(id).get()
person.name = "Joe"
personRepository.saveAndFlush(person)
}
private fun <T> executeInNewTransaction(action: () -> T): T {
val transactionTemplate = TransactionTemplate(platformTransactionManager)
transactionTemplate.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRES_NEW
return try {
transactionTemplate.execute {
action()
}!!
} catch (e: Exception) {
throw e
}
}
}
@Entity
@EntityListeners
class Person {
@Id
@GeneratedValue
var id: Long = 0L
@Column
var name: String = ""
@Version
var version: Long = 0L
}
interface PersonRepository : JpaRepository<Person, Long>
fun main(args: Array<String>) {
runApplication<DbOptimiticLockingApplication>(*args)
}
What is the reason why spring try to commit?
当从数据库中读取一个实体时,对于 JPA 层,它变成一个 persistent
或称为 managed
实体。
处于 persistent/managed
状态的实体由 ORM 供应商观察,对它们所做的任何更改都会自动传递到数据库层。
发生这种情况的前提是实体被视为 persistent/managed
的方法没有任何错误地完成并且该方法属于 JPA 事务 !
对于您描述的以下引用,不,它没有按预期工作,您观察到的行为和您期望发生的事情只是巧合。
transactionByAnnotation work as expected and no changes for variable
personTransaction1 were commited
@Transactional(Transactional.TxType.REQUIRES_NEW)
fun transactionByAnnotation(id: Long) {
var personTransaction1 = personRepository.findById(id).get()
@Transactional(Transactional.TxType.REQUIRES_NEW)
-> 在您在这里提到的这种情况下完全被忽略,因为该方法的调用来自方法 run
的同一实例,因此根本不考虑注释。 More info here in an old SO question
因此,由于此方法完成时没有任何错误但不属于任何 JPA 事务,因此在托管实体上所做的更改不会自动传递到数据库中。
如果它属于 JPA 事务并且 @Transactinal
没有因为自调用而被忽略,它会在数据库中保留更改。
Spring Boot 尝试自动提交实体 personTransaction1 的 manuell 事务中的更改,而不调用存储库中的保存方法。 personTransaction1 中的更改已提交。方法manuellTransaction抛出org.springframework.orm.ObjectOptimisticLockingFailureException。在方法 transactionByAnnotation 中使用基于注释的事务处理的相同代码按预期工作,并且没有提交变量 personTransaction1 的更改。 spring 尝试提交的原因是什么?
@SpringBootApplication
@EnableJpaAuditing
class DbOptimiticLockingApplication : CommandLineRunner {
@Autowired
private lateinit var personRepository: PersonRepository
@Autowired
private lateinit var platformTransactionManager: PlatformTransactionManager
override fun run(vararg args: String?) {
executeInNewTransaction {
personRepository.deleteAll()
}
val person = createPerson()
transactionByAnnotation(person.id)
manuellTransaction(person.id)
}
@Transactional
fun createPerson(): Person {
val person = Person()
person.name = "Max"
return personRepository.save(person)
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
fun transactionByAnnotation(id: Long) {
var personTransaction1 = personRepository.findById(id).get()
// don't trigger commit
personTransaction1.name = "Tom"
transaction11ByAnnotation(personTransaction1.id)
transaction12ByAnnotation(personTransaction1.id)
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
fun transaction11ByAnnotation(id: Long) {
businessLogic1(id)
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
fun transaction12ByAnnotation(id: Long) {
businessLogic2(id)
}
fun manuellTransaction(id: Long) {
executeInNewTransaction {
var personTransaction1 = personRepository.findById(id).get()
// trigger commit
personTransaction1.name = "Tom"
manuellTransaction11(personTransaction1.id)
manuellTransaction12(personTransaction1.id)
}
}
fun manuellTransaction11(id: Long) {
executeInNewTransaction {
businessLogic1(id)
}
}
fun manuellTransaction12(id: Long) {
executeInNewTransaction {
businessLogic2(id)
}
}
private fun businessLogic1(id: Long) {
val person = personRepository.findById(id).get()
person.name = "Martin"
personRepository.saveAndFlush(person)
}
private fun businessLogic2(id: Long) {
val person = personRepository.findById(id).get()
person.name = "Joe"
personRepository.saveAndFlush(person)
}
private fun <T> executeInNewTransaction(action: () -> T): T {
val transactionTemplate = TransactionTemplate(platformTransactionManager)
transactionTemplate.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRES_NEW
return try {
transactionTemplate.execute {
action()
}!!
} catch (e: Exception) {
throw e
}
}
}
@Entity
@EntityListeners
class Person {
@Id
@GeneratedValue
var id: Long = 0L
@Column
var name: String = ""
@Version
var version: Long = 0L
}
interface PersonRepository : JpaRepository<Person, Long>
fun main(args: Array<String>) {
runApplication<DbOptimiticLockingApplication>(*args)
}
What is the reason why spring try to commit?
当从数据库中读取一个实体时,对于 JPA 层,它变成一个 persistent
或称为 managed
实体。
处于 persistent/managed
状态的实体由 ORM 供应商观察,对它们所做的任何更改都会自动传递到数据库层。
发生这种情况的前提是实体被视为 persistent/managed
的方法没有任何错误地完成并且该方法属于 JPA 事务 !
对于您描述的以下引用,不,它没有按预期工作,您观察到的行为和您期望发生的事情只是巧合。
transactionByAnnotation work as expected and no changes for variable personTransaction1 were commited
@Transactional(Transactional.TxType.REQUIRES_NEW)
fun transactionByAnnotation(id: Long) {
var personTransaction1 = personRepository.findById(id).get()
@Transactional(Transactional.TxType.REQUIRES_NEW)
-> 在您在这里提到的这种情况下完全被忽略,因为该方法的调用来自方法 run
的同一实例,因此根本不考虑注释。 More info here in an old SO question
因此,由于此方法完成时没有任何错误但不属于任何 JPA 事务,因此在托管实体上所做的更改不会自动传递到数据库中。
如果它属于 JPA 事务并且 @Transactinal
没有因为自调用而被忽略,它会在数据库中保留更改。