Spring Boot tries to auto commit changes in manuell transaction for entity personTransaction1 without calling save method in repository. Changes in personTransaction1 are commited. The method manuellTransaction throw org.springframework.orm.ObjectOptimisticLockingFailureException. The same code with annotation based transaction handling in method transactionByAnnotation work as expected and no changes for variable personTransaction1 were commited. What is the reason why spring try to commit?
@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)
}