Какой класс должен отвечать за начало / окончание транзакции в JPA?

Итак, у меня есть пример кода, как это:

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

}

и вот как я его тестирую:

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

}

Это все отлично работает. Мой вопрос об ООП.

Я решил, что BookDaoImpl не должен отвечать за получение EntityManager. Это должно быть ответственностью BookService. Зачем? Я действительно не знаю.

Кроме того, кто должен отвечать за получение транзакции, начало и фиксацию? Снова BookService или BookDao?

2 ответа

Решение

Транзакция JPA должна управляться на уровне сервиса. Вот контрпример: рассмотрим, у вас есть find метод на вашем уровне DAO:

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

А вашему классу Book принадлежит коллекция страниц:

@OneToMany(mappedBy = "book", fetch = LAZY")
private Set<Page> pages;

public Set<Page> getPages() {
    return pages;
}

Если entityManager имеет жизненный цикл в DAO, вызов getPages() метод из вашего сервисного уровня приведет к исключению отложенной инициализации

Конечно, в каждом правиле есть исключения, но в целом вы должны управлять своей транзакцией на уровне сервиса (или уровне репозитория в зависимости от формулировки). Вы даже можете использовать ОБЯЗАТЕЛЬНЫЙ атрибут демаркации транзакции на уровне DAO, чтобы сделать его обязательным.

Мое мнение, это нормально, что BookDao знает о EntityManager, так как он о способе сохранения данных. Относительно транзакции - это ответственность сервисного уровня, поскольку она отвечает за реализацию бизнес-логики, а границы транзакций определяются в бизнес-требованиях. Однако было бы замечательно реализовать управление транзакциями независимо от технологии персистентности (теперь вы используете JPA, завтра JDBC, позже что-то еще). Думаю, аннотации Spring Spring могут быть хорошим примером такого подхода.

Другие вопросы по тегам