0

Being new to JPA and transaction management, I have a Spring Boot REST API application layered like this:

Controller -> Service -> DAO -> DB

I want to be agnostic from any ORM and am using only JPA annotations and concepts like Entity Manager, PersistenceContext etc. Having a DAO layer, my question is where should I manage transaction logic: in DAO? In the Service layer? For now my DAO is implementing standard CRUD logic like:

public void save(IEntityObject entity){
        entityManager.getTransaction().begin();
        entityManager.persist(entity);
        entityManager.getTransaction().commit();
        entityManager.close();
    }

//other CRUD methods

But this makes a lot of duplicate code! So is it a good idea to put the transaction management related logic in the service above in a generic method, but how? What about rollback logic? Is there any standard pattern for this? Thank you

3 Answers 3

1

Transactions are very dependent on the use case. While there is no standard architecture, putting transaction logic in the layer of the application that has the most knowledge about the use case will give you the fewest headaches.

The advantage of placing transaction logic in the DAO is that you hide these data access details from the rest of the application. The downside is that your DAO is not very knowledgeable about the use case, so it has a hard time understanding when to start, commit, or roll back transactions. The DAO should provide general purpose data access logic that could be reused in multiple use cases.

With a service layer, I expect controllers to handle the semantics of the HTTP request-response lifecycle along with knowing which use case should be invoked. That leaves the service layer to handle business logic, coordination logic, validation, and transaction management. The trick is to create an abstraction to hide the specifics of your ORM, yet still acknowledge that a transaction is necessary.

While the following seems more prevalent in C#, the Unit of Work design pattern might be a good fit.

A unit of work is a behavioral pattern in software development. Martin Fowler has defined it as everything one does during a business transaction which can affect the database. When the unit of work is finished, it will provide everything that needs to be done to change the database as a result of the work.

Source: Wikipedia

If you research the unit of work pattern, don't get too hung up on the repository pattern. You will see a lot of references to that. Simply replace "repository" with "Data Access Object" and you should be fine. The important bits are:

  • Define an interface for a specific unit of work.
  • The interface should define methods to commit or roll back the transaction.
  • The interface should not require types from your data access layer, if you desire strict decoupling of those concerns.
  • Include all data access methods necessary to complete a particular use case.

Internally, the unit of work can reference any data access objects it needs. Don't think of this as a one-to-one mapping of DAOs with additional "commit" and "rollback" methods. The DAOs are an implementation detail of the unit of work that doesn't need to be exposed to the rest of the application. And don't get too hung up on naming things, either. I've seen unit of work implementations that dance around "commit" and "rollback" using methods named like "saveChanges" or "discardChanges". Relational databases don't have ownership over simple and concise terms like commit and rollback. The main point is to decouple data access from use case logic, rather than pretending a database doesn't exist.

1
  • Great, I wasn't aware of this UoW pattern, bt it seems matching my needs, thanks!
    – DAN
    Commented Sep 16 at 11:19
0

Controller -> Service -> DAO -> DB

It depends on how responsibilities has been defined. If DAO per database table is used then a DAO (data access object) responsibility is to preserver data integrity of a database table - a common technique is locking, optimistic or pessimistic - that would make Service responsible to preserver business data model integrity - a common technique is transaction, that could involve other resources besides database.

0
  1. When you dive in depth, you will understand that transaction is not something you could wrap inside a simple save method.
  2. Transactions operate on service layer. Because there will be cases where you will need to save two or more Entities together to complete a transaction.
  3. Then there are distributed transactions where multiple databases need to co-ordinate to complete a transaction..

Spring itself has it's own declarative transaction management. It is proven, comprehensive and used everyday in every enterprise applications.

Search for tutorials related to @transactional annotation. Or read Spring Documentation

Example:

Enable Spring Transaction Configuration in application

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
       //...
   }

   @Bean
   public PlatformTransactionManager transactionManager() {
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
      return transactionManager;
   }
}

Then wrap the service layer with a transactional annotation

@Service
@Transactional
public class FooService {
    //...
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.