Hibernate 2# - physical vs logical transactions

There are often some confusions in the regard of using 'transaction' term, especially when talking about ORM solutions like Hibernate. Sometimes transaction can have a different meaning depending on the context. It needs to be understood in order to be able to prevent any data anomalies during web development. We will have a look on what are the differences between physical and logical transactions. 


Physical transaction

Physical transaction is the actual transaction that takes place on the database level. It means that the beginning of the physical transaction is when actual transaction on the database level is started. The end of the physical transaction takes place when this transaction is commited or rollbacked on the database level. Atomicity of operations made during physical transaction is guarded by database itself.

Logical transaction

Logical transactions on the other hand are those handled on the the application side, for example Spring application. Logical transaction is an abstracion used on the application side to mark what actions should be performed as atomic. In case of Spring, underneath this abstraction Hibernate implements handling of actual physical transactions. It is done by communicating with database via JDBC Connection. Defining logical transactions declaratively in Spring can be done with @Transactional annotation. Logical transaction in Spring does not always equal one physical transaction, for example multiple logical transactions can share the same physical transaction. We will soon analize it further.

Physical vs Logical - the same boundaries

Lets have a look at an example below, with typical case of managing transaction in Spring:


We are using @Transactional annotation. Hibernate underneath opens a new physical database transaction when we enter the method, and commits it when we exit the method (or rollbacks in case of an error). In the presented case both physical and logical transactions have the same boundaries - saveNewClient service method. Opening and closing logical transaction takes place at the same time as opening and closing the physical one. That is a very common case, which probably leads to using physical and logical terms interchangeably. We will see however, that there are distictions between those two transaction levels.


Multiple logical transactions sharing one physical transaction

We have modified an example of saveNewClient method, which now looks as follows:


As you can notice, we invoke another method from different service during the transaction - registerNewAccount. Here is its implementation:


Apparently, this method is also annotated as @Transactional. Since the default propagation of transactions in Spring is 'PROPAGATION_REQUIRED', it means that each method annotated as @Transactional will open a new logical transaction and:
  • open a new physical transaction if no physical transaction exists yet. 
  • reuse already existing physical transaction if it exists. 
That means that there will be only one physical transaction, and it will be opened and closed at the time of opening and closing the most outer logical transaction (connected with saveNewClient method). In our example it means that entering saveNewClient method opens logical and physical transactions but entering registerNewAccount method opens only new logical transaction and reuses physical one. We will have two logical transactions sharing one physical transaction.

You could wonder, what is the benefit of this approach, why shouldn't we always map one logical transaction to one physical? Since the outer transaction scope is logically independent from the inner one, there can be a different rollback-only status marked for each logical transaction. More on the rollback-only status behaviour can be found here - Spring Transactions docs.

The benefit of marking both methods as @Transactional is also the fact that we can invoke both of them separately, and in this case there would be two separate logical and physical transactions. That means that logical transaction can behave in a flexible way in the regard of physical transaction - it can create new physical transaction or reuse one already existing - it depends on the transactional method call context. 


Logical Long Conversation transaction

There is also an application level type of transaction which is used in so called Long Conversations. We can ascribe it to logical transactions, however this is a special one. As an application level transaction used in Long Conversation we can imagine a sequence of actions that should behave as atomic from application/business level perspective, however these actions are not always atomic from physical transaction point of view. The reason for it is that in case of Long Conversations, the transaction can span over multiple physical transacions. This kind of transaction is often described as Unit of Work, to ephasize its rather abstract meaning of transaction. 

Lets move to the example. Consider the following business scenario:
  1. I check my bank account saldo (GET request to the application server).
  2. Based on the saldo I perform a transfer from my account to some other account (POST request to the server).
From the application point of view that is one business transaction. That is not acceptable however that it could be one physical transaction, since it would have serious performance drawbacks due to lock contention. The physical transaction would have to be opened while user reads his saldo and tries to make a decision how big transfer can he make. Nevertheless, from the business perspective it must be considered as an atomic Unit of Work. Why? If some service would transfer money from my account after I check my saldo, but before I perform a transfer, then I could perform a transfer based on an outdated saldo. One of anomalies that it could lead to is an infamous Lost Update anomaly. In the presented example there would be two physical transactions - one for GET request (read data) and the other one for POST (modify data). There are solutions that enable us to develop application server in a way, that those two actions (read saldo, make transfer) effectively behave as atomic. There is however a condition - this Unit of Work can contain maximum one physical transaction that updates (modifies/inserts/deletes) data. It is the money trafser in this case. To develop this business flow as an atomic Unit of Work in Hibernate we can follow two patterns:
  1. session-per-request-with-detached-objects
  2. session-per-conversation
Details regarding the patterns can be found here: Hibernate docs.

Conclusions

The differences between logical and physical transactions are:
  1. Physical transaction concerns database transaction while logical transaction is an abstract definition of atomic sequence of actions from the application point of view.
  2. Logical transaction can contain the same sequence of actions as physical transaction but not always:
    1. Multiple logical transactions can share one physical transaction.
    2. One logical transaction can span over multiple physical transactions (Long Conversation Unit of Work).
The special kind of logical transaction - application level transaction used in Long Conversations describes serious of connected actions that should behave as atomic but are not always atomic from the database point fo view. In this case one logical application level transaction spans over multiple physical transactions.

Komentarze

Popularne posty z tego bloga

Spring Data 1# - how repositories work under the hood

Hibernate 1# - entity states overview

Data consistency and performance in web applciation