【问题标题】:Spring Boot auto commit manuell transaction without call save on repositorySpring Boot自动提交手动事务,无需调用存储库
【发布时间】:2022-01-07 12:16:51
【问题描述】:

Spring Boot 尝试在实体 personTransaction1 的手动事务中自动提交更改,而不调用存储库中的保存方法。 personTransaction1 中的更改已提交。 ma​​nuellTransaction 方法抛出 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)
}

【问题讨论】:

    标签: spring-boot jpa spring-data-jpa


    【解决方案1】:

    spring 尝试提交的原因是什么?

    当从数据库中读取实体时,对于 JPA 层,它会变成 persistent 或称为 managed 实体。

    persistent/managed 状态下的实体由 ORM 供应商观察,对它们所做的任何更改都会自动传递到数据库层。 发生这种情况的前提是实体被视为persistent/managed 的方法在没有任何错误的情况下完成,并且该方法属于JPA 事务

    对于您描述的以下引用,不,它没有按预期工作,您观察到的行为和您期望发生的事情只是巧合。

    transactionByAnnotation 按预期工作,变量没有变化 personTransaction1 已提交

        @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 没有因为自调用而被忽略,则它会将更改保留在数据库中。

    【讨论】:

    • 是否可以停用此自动行为
    • 如果您的方法不属于 JPA 事务,则托管实体上的更改将不会自动保存在数据库中
    猜你喜欢
    • 2019-11-25
    • 2020-12-01
    • 1970-01-01
    • 2017-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多