Какой лучший способ обновить сущность JPA (Hibernate): транзакционный или нетранзакционный и почему? - PullRequest
5 голосов
/ 09 марта 2020

У меня есть ситуация, когда мне приходится выбирать между двумя вариантами, и мне не ясно, в чем разница между этими вариантами. Я буду очень благодарен, если кто-нибудь сможет объяснить мне, какой мне выбрать и почему. Короче говоря, у меня есть простая сущность JPA (Kotlin язык):

@Entity
@Table(name = "account")
data class AccountEntity(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long,

    var balance: Int,

    @ManyToOne
    var accountType: AccountTypeEntity
)

А в слое business-logi c я хочу иметь метод для обновления баланса счета по его accountId. В основном мне нужно загрузить сущность учетной записи по идентификатору, затем установить новый баланс и, наконец, использовать метод сохранения, предоставленный Hibernate. Но я также обнаружил тот факт, что мне не нужно вызывать метод save в явном виде, если мой метод будет помечен @transactional. Итак, с этого момента у меня есть два варианта

Первый

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
}

Во-первых, для меня не ясно, в чем будет разница этих варианты с точки зрения базы данных. Не могли бы вы уточнить это?

Во-вторых, я думаю, что в таком методе мне вообще не нужен TRANSACTION (с точки зрения базы данных), потому что я делаю только одну операцию записи и для меня это выглядит избыточным использовать, чтобы избежать вызова метода сохранения Hibernate в явном виде. Но, может быть, я ошибаюсь и есть некоторые причины использовать транзакцию даже здесь. Поэтому, пожалуйста, поправьте меня.

1 Ответ

1 голос
/ 09 марта 2020

В этом случае разница практически отсутствует. В первом примере также создается транзакция, так как она будет создана вызовом save (), когда нет выполняемой транзакции для принятия. Он будет жить так же долго, как и вызов save (). Во втором примере вы создаете транзакцию самостоятельно, которая в основном будет жить столько же, сколько и вызов метода. Поскольку в этих методах почти нет логики c, их площадь будет в основном одинаковой.

Это не лучший пример, чтобы попытаться понять это, поскольку это слишком просто c. Ситуация становится более интересной, когда вы выполняете более сложные обновления сущности, которые могут касаться нескольких таблиц и записей, и в то же время, особенно когда вы начинаете вносить изменения, которые вызывают каскадное сохранение, обновления и удаления, происходящие при изменении коллекции OneToMany. .

Представьте себе систему, которая обрабатывает заказы. заказы имеют строки заказа. И заказы привязаны к счетам. И строки заказа привязаны к счетам-фактурам. И, возможно, у заказов есть родительские заказы, потому что они сгруппированы вместе. И платежи делятся на бронирования и строки бронирования, которые привязаны к заказам, линиям заказа, счетам и счетам. Представьте, что делает такая иерархия объектов в одном выражении save ().

В таких случаях становится все более понятно, почему такая функция, как save (), все еще создает транзакцию; этот один вызов save () может по-прежнему представлять где-то от одного до тысячи выполняемых операторов, в зависимости от сложности иерархии объектов. Наличие возможности откатить все изменения в случае сбоя является обязательным.

Когда вы начнете работать с такой структурой сущностей, вы, скорее всего, будете стремиться использовать установку @Transactional довольно быстро, так как вы будете рано или поздно сталкиваясь с печально известной ленивой ошибкой инициализации .

...