Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > JPA и транзакции


Автор: Asal 21.8.2008, 14:37
Возник вопрос аналогичный вопросу из http://forum.vingrad.ru/forum/topic-124877.html#st_150_view_0.
где же открывать транзакцию и делать commiy() ?

Принцип почти такой же как и у Infinite из сереала.

Сначало инициализируется класс Controller
Код

contextLoader = new ContextLoader();
WebApplicationContext context = contextLoader.initWebApplicationContext(servletContextEvent.getServletContext());
controller = Controller.getInstance();
controller.init(context);

В классе Controller инициализурую менеджеров
Код

  public void init(WebApplicationContext context) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("JetscartUnit");
    entityManager = emf.createEntityManager();

    registry = (Registry) context.getBean(Registry.BEAN_NAME);
    initManagers();
  }
  public void initManagers() {
    Collection<Manager> c = registry.getManagers().values();
    for (Manager m : c) {
      m.init();
    }
  }

В Registry хранятся менеджеры
Код

package my.shop.core;

import java.util.HashMap;

public class Registry {
  public static final String BEAN_NAME = "registry";

  private HashMap<String, Manager> managers = new HashMap<String, Manager>();

  public Manager getManager(String name) {
    return managers.get(name);
  }

  public HashMap<String, Manager> getManagers() {
    return managers;
  }

  public void setManagers(HashMap<String, Manager> managers) {
    this.managers = managers;
  }

  public void clear() {
    managers.clear();
  }
}

А уже в менеджерах выполню функции insert/update/delete
Код

package my.shop.user;

import my.shop.core.ResultStatus;
import my.shop.core.BasicManager;
import my.shop.core.Manager;

import javax.persistence.Query;
import javax.persistence.NoResultException;


public class UserManager extends BasicManager implements Manager {
  public static final String BEAN_NAME = "usersManager";

  public UserManager() {
    super();
  }

  public void init() {
    System.out.println("UserManager Init()");
    super.init();
  }

  public void destroy() {
    super.destroy();
  }

  public UserEntity login(String username, String password) throws NoResultException {
    Query query = entityManager.createNamedQuery("loginUser");
    query.setParameter("username", username);
    query.setParameter("password", password);
    return (UserEntity) query.getSingleResult();
  }

  public ResultStatus insert(UserEntity user) {
    entityManager.getTransaction().begin();
    Query query = entityManager.createNamedQuery("containsUser");
    query.setParameter("username", user.getUsername());
    if (!query.getResultList().isEmpty() || entityManager.contains(user)) {
      System.out.println("user exist");
      return ResultStatus.newErrorResult();
    }
    entityManager.persist(user);
    entityManager.getTransaction().commit();
    return ResultStatus.newOkResult();
  }

  public ResultStatus update(UserEntity user) {
    entityManager.getTransaction().begin();
    entityManager.flush();
    entityManager.refresh(user);
    entityManager.getTransaction().commit();
    return ResultStatus.newOkResult();
  }
  public ResultStatus delete(UserEntity user) {
    entityManager.getTransaction().begin();
    entityManager.flush();
    entityManager.remove(user);
    entityManager.getTransaction().commit();
    return ResultStatus.newOkResult();
  }
}

Друзья помогите решить задачу.
Где открывать транзакцию?

Исходный код во вложении.

Автор: ivg 21.8.2008, 18:48
Цитата(Asal @  21.8.2008,  17:37 Найти цитируемый пост)
Где открывать транзакцию?
 Какую транзакцию? Строки 35, 43, 48, 51, 55, 58 последнего листинга (класс UserManager)? Не? Не то? Тогда что?

Автор: mbasil 21.8.2008, 19:33
О чем  вопрос? Вы уже и открываете, и закрываете следующими вызовами:
Код

entityManager.getTransaction().begin();
...
entityManager.getTransaction().commit();


По сути у Вас каждая транзакция состоит из одной операции в одном методе.

Автор: Asal 21.8.2008, 19:51
Цитата(ivg @  21.8.2008,  18:48 Найти цитируемый пост)
Какую транзакцию? Строки 35, 43, 48, 51, 55, 58 последнего листинга (класс UserManager)? Не? Не то? Тогда что?

Тем кто читал сериал я думаю будет более или менее понятно о чем речь.
А для тех кто не читал попытаюсь объяснить.

Допустим на страничке мы пытаемся изменить данные юзера, делаем запрос.
В классе UserController обрабатываем этот запрос
Код

  public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    UserManager userManager = (UserManager) com.jetscart.core.Controller.getInstance().getRegistry().getManager(UserManager.BEAN_NAME);
    UserEntity user = new UserEntity();
//... что-то делаем
    System.out.println("insert user");
    userManager.update(user);
    return new ModelAndView();
  }

В классе UserManager обновляем в базе
Код

  public ResultStatus update(UserEntity user) {
    entityManager.getTransaction().begin();
    entityManager.flush();
    entityManager.refresh(user);
    entityManager.getTransaction().commit();
    return ResultStatus.newOkResult();
  }

Если приходит только один запрос, то все нормально, но когда одну запись о юзере пытаются поменять сразу двое (и более),
возникает проблема... И начинают сыпаться разного рода ошибки
например
Код

21.08.2008 19:46:10 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet test threw exception
java.lang.IllegalStateException: Transaction already active
    at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:35)
    at com.jetscart.user.UserManager.update(UserManager.java:55)
    at com.jetscart.user.UserController.handleRequest(UserController.java:25)
    at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:809)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

Вот и вопрос где, как, когда открывать транзакции, делать commit и т.д.

P.S.
http://forum.vingrad.ru/forum/topic-204182.html мне посоветовали почитать книгу http://depositfiles.com/ru/files/2729004. 
Там по-моему глава есть про Web приложения.

Автор: ivg 21.8.2008, 20:20
Проблема не в транзакциях, а в расшареном между потоками javax.persistence.EntityManager
Цитата

A EntityManagerFactory is an expensive-to-create, threadsafe object intended to be shared by all application threads. It is created once, usually on application startup.

An EntityManager is an inexpensive, non-threadsafe object that should be used once, for a single business process, a single unit of work, and then discarded. An EntityManager will not obtain a JDBC Connection (or a Datasource) unless it is needed, so you may safely open and close an EntityManager even if you are not sure that data access will be needed to serve a particular request. (This becomes important as soon as you are implementing some of the following patterns using request interception.)
 http://www.hibernate.org/hib_docs/entitymanager/reference/en/html_single/#transactions-basics Ссылка для Hibernate, возможно для других провайдеров JPA это не так, надо смотреть спецификацию.

Автор: Asal 22.8.2008, 10:04
Может кто-то посоветует framework для работы с транзакциями ?

Автор: alexadr 22.8.2008, 11:15
Я бы предложил оборачивать все действия с персистентными объектами, - сессионными бинами, при этом у соответствующих методов достаточно будет указать параметр(Аннотацию) для тразкации. А EntityManager получать в бине с помощью dependency injection.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)