5

I have a situation where I have to make a choice between two options and it's not clear for me what is the difference between that options. I will be very thankful if somebody could explain to me which one should I choose and why. Long story short I have a simple JPA entity (Kotlin language):

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

    var balance: Int,

    @ManyToOne
    var accountType: AccountTypeEntity
)

And in business-logic layer I want to have a method for updating account balance by its accountId. Basically I need to load account entity by id, then set new balance and lastly use save method which is provided by Hibernate. But I also found the fact that I don't need to call save method in explicit form if my method will be annotated with @transactional. So from that point I have two options

First one

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

Second one

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

Firstly, for me it's not clear what will be the difference of those options in terms of database. Could you please clarify it?

Secondly, I think that in such method I don't really need TRANSACTION (in terms of database) at all because I make only one 'write' operation and for me it looks redundant to use to avoid calling hibernate save method in explicit form. But may be I'm wrong and there are some reasons to use transaction even here. So please correct me.

  • You cannot persist entities into the database without a transaction. Older versions of Spring Data JPA make the repository methods transactional which is probably why the first sample works. – M. Deinum Mar 09 '20 at 09:36

1 Answers1

2

The difference is almost none in this case. The first example also creates a transaction as it will be created by the save() call when there is no running transaction to adopt. It will live for as long as the save() call. In the second example you create a transaction yourself, which will basically live for just as long as the method invocation. Since there is almost no logic in these methods their footprint will be mostly identical.

This is not a great example to try and figure this out as it is too simplistic. Things will get more interesting when you perform more complicated updates to the entity which may touch multiple tables and records and the same time, especially when you start to do changes which will cause cascaded persists, updates and deletes to happen when you modify a OneToMany collection.

Imagine a system which processes orders. orders have orderlines. And orders are tied to invoices. And orderlines are tied to invoicelines. And maybe orders have parent orders because they're grouped together. And payments are split in bookings and bookinglines which are tied to the orders, orderlines, invoices and invoicelines. Imagine what such an entity hierarchy does in a single save() statement.

In such cases it is all the more clear why a function such as save() still creates a transaction; that one save() call can still represent anywhere between one and thousands of statements being executed depending on the complexity of the entity hierarchy. Having the possibility to rollback all changes in case of a failure is a must.

When you start to work with such an entity structure, you will likely gravitate to using a @Transactional setup quite quickly as you will be running into the infamous lazy initialization error sooner or later.

Gimby
  • 4,565
  • 2
  • 32
  • 44