Какой класс должен отвечать за начало / окончание транзакции в 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 могут быть хорошим примером такого подхода.