简体   繁体   中英

Spring Boot auto commit manuell transaction without call save on repository

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)
}

What is the reason why spring try to commit?

When an entity is read from Database it becomes for the JPA layer a persistent or otherwise called managed entity.

Entities on persistent/managed state are observed by the ORM vendor and any changes being done on them are passed in the database layer automatically. Perquisite for this to happen is that the method where the entity is considered as persistent/managed finishes without any errors and the method belongs to a JPA transaction !

在此处输入图像描述

For the following quote that you describe, no it does not work as expected and the behavior that you observe and what you expect to happen is just a coincidence.

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) -> is completely ignored in this case that you mention here since the invocation of that method is from the same instance from method run , so the annotation is not considered at all. More info here in an old SO question

So since this method finishes wihout any errors but does not belong to any JPA transaction , the changes being done on managed entity are not passed automatically in Database.

If it belonged into a JPA transaction and the @Transactinal was not ignored because of self invocation it would have persisted the changes in Database.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM