javaoopresponsibility

Which class should be responsible for starting / ending transaction in JPA?


So I have a sample code like this:

package biz.tugay.books10Aug.dao;
/* User: koray@tugay.biz Date: 10/08/15 Time: 22:54 */

import biz.tugay.books10Aug.model.Book;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

public class BookDaoImpl implements BookDao {

    private EntityManager entityManager;

    public BookDaoImpl(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public void persist(Book book) {
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        entityManager.persist(book);
        transaction.commit();
    }

}

and this is how I unit test it:

package biz.tugay.books10Aug.dao;
/* User: koray@tugay.biz Date: 10/08/15 Time: 22:56 */

import biz.tugay.books10Aug.model.Book;
import org.junit.Test;

import javax.persistence.EntityManager;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class BookDaoImplTest {

    @Test
    public void testPersistNewBook() throws Exception {
        PersistenceUtil.initalizeEntityManagerFactory();
        EntityManager entityManager = PersistenceUtil.getEntityManager();
        BookDao bookDao = new BookDaoImpl(entityManager);
        String isbn = new SimpleDateFormat("HHmmss").format(Calendar.getInstance().getTime());
        Book book = new Book();
        book.setIsbn(isbn);
        book.setName("Just Another Book in the DB, Volume: " + isbn);
        book.setPrice(10);
        book.setPublishDate(Calendar.getInstance().getTime());
        book.setPublisher("002");
        bookDao.persist(book);
    }

}

This all works fine. My question is about OOP.

I decided that BookDaoImpl should not be responsible for getting the EntityManager. It should be the BookService 's responsibility. Why? I a do not know really.

Also, whos responsibility should be to get transaction, begin and commit? Again BookService or BookDao?


Solution

  • JPA Transaction should be managed on a service layer. Here's an counterexample: consider you have a find method in your DAO layer:

    public Book find(long id) {
          return entityManager.find(Book.class, id);
    }
    

    And your Book class owns a collection of pages:

    @OneToMany(mappedBy = "book", fetch = LAZY")
    private Set<Page> pages;
    
    public Set<Page> getPages() {
        return pages;
    }
    

    If the entityManager has a lifecycle within a DAO, the call of getPages() method made from your service layer will result in lazy initialization exception

    Of course in each rule there are exceptions, but in general you should manage your transaction on service layer (or repository layer depending on wording). You can even use MANDATORY transaction demarcation attribute in your DAO layer in order to make it mandatory.