Spring 更新JPA(Hibernate)实体的更好方法是什么:事务性还是非事务性?为什么?

Spring 更新JPA(Hibernate)实体的更好方法是什么:事务性还是非事务性?为什么?,spring,hibernate,kotlin,spring-data-jpa,Spring,Hibernate,Kotlin,Spring Data Jpa,我有一种情况,我必须在两种选择之间做出选择,我不清楚这两种选择之间有什么区别。如果有人能向我解释我应该选择哪一个以及为什么,我将非常感激。 长话短说,我有一个简单的JPA实体(Kotlin语言): 在业务逻辑层中,我希望有一个通过accountId更新帐户余额的方法。基本上我需要按id加载帐户实体,然后设置新的余额,最后使用Hibernate提供的save方法。但我还发现,如果我的方法将被@transactional注释,那么我不需要以显式形式调用save方法。所以从这一点来看,我有两个选择 第

我有一种情况,我必须在两种选择之间做出选择,我不清楚这两种选择之间有什么区别。如果有人能向我解释我应该选择哪一个以及为什么,我将非常感激。 长话短说,我有一个简单的JPA实体(Kotlin语言):

在业务逻辑层中,我希望有一个通过accountId更新帐户余额的方法。基本上我需要按id加载帐户实体,然后设置新的余额,最后使用Hibernate提供的save方法。但我还发现,如果我的方法将被@transactional注释,那么我不需要以显式形式调用save方法。所以从这一点来看,我有两个选择

第一个

fun updateAccountBalance(id: Long, balance: Int) {
    val account = accountRepository.findById(id).orElseThrow { RuntimeException("No account with id=$id found") }
    account.balance = balance
    accountRepository.save(account)
}
第二个

@Transactional
fun updateAccountBalance(id: Long, balance: Int) {
    val account = accountRepository.findById(id).orElseThrow { RuntimeException("No account with id=$id found") }
    account.balance = balance
}
首先,对于我来说,不清楚这些选项在数据库方面有什么不同。你能澄清一下吗


其次,我认为在这种方法中,我根本不需要事务(就数据库而言),因为我只做了一个“写”操作,而且对我来说,避免以显式形式调用hibernate save方法看起来是多余的。但可能是我错了,即使在这里也有一些使用事务的理由。所以请纠正我。

在这种情况下,差异几乎没有。第一个示例还创建了一个事务,因为在没有可采用的运行事务时,save()调用将创建该事务。它将与save()调用一样有效。在第二个示例中,您自己创建了一个事务,该事务基本上与方法调用一样有效。因为在这些方法中几乎没有逻辑,所以它们的足迹基本相同

这不是一个很好的例子,因为它太简单了。当您对实体执行更复杂的更新时,事情会变得更有趣,这些更新可能同时涉及多个表和记录,尤其是当您开始进行更改时,这些更改将导致在修改OneToMany集合时发生级联持久化、更新和删除

想象一个处理订单的系统。订单有订单行。订单与发票挂钩。订单行与发票行绑定。也许订单有父订单,因为它们是组合在一起的。付款在与订单、订单行、发票和发票行关联的预订和预订行中进行拆分。想象一下这样一个实体层次结构在单个save()语句中的作用

在这种情况下,save()之类的函数仍然创建事务的原因就更加清楚了;根据实体层次结构的复杂性,一个save()调用仍然可以表示正在执行的一条到数千条语句之间的任意位置。在发生故障时,必须能够回滚所有更改


当您开始使用这样的实体结构时,您可能会很快被吸引到使用
@Transactional
设置,因为您迟早会遇到臭名昭著的问题。

如果没有事务,您无法将实体持久化到数据库中。SpringDataJPA的旧版本使存储库方法具有事务性,这可能就是第一个示例工作的原因。
@Transactional
fun updateAccountBalance(id: Long, balance: Int) {
    val account = accountRepository.findById(id).orElseThrow { RuntimeException("No account with id=$id found") }
    account.balance = balance
}