hibernate / jpa с жалобами "сбрасывать во время каскада" - PullRequest
0 голосов
/ 16 февраля 2019

У меня сбой приложения с приведенной ниже ошибкой:

org.hibernate.HibernateException: Flush during cascade is dangerous

Я не сбрасываю, если hibernate не делает это от моего имени.

Характеристики:

  • webapp на tomcat
  • hibernate / jpa для персистентности (менеджер управляемого объекта приложения)

Это код моего класса утилит для управления менеджером объекта:

private static EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("returnit");

private static EntityManager entityManager;

public static EntityManager getEntityManager(){

    return entityManager;
}

public static EntityManager initEntityManager(){
  if (emFactory == null) {
      emFactory = Persistence.createEntityManagerFactory( "returnit" );
  }

  entityManager = emFactory.createEntityManager();
  return entityManager;
}

И этот метод вызывает ошибку:

@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response post(@HeaderParam(HttpHeaders.AUTHORIZATION) String authHeader, MasterCrossDock mcd) {

    EntityManager em = Utils.initEntityManager();
    em.getTransaction().begin();

    MasterCrossDockDAO.save(mcd);

    em.getTransaction().commit();
    em.close();

    return Response.ok(mcd.getId()).build();
}

public static void save(MasterCrossDock new_mcd) {  

    List<Receptacle> receptacles = new_mcd.getReceptacles();

    List<Long> ids = new ArrayList<Long>();

    for (Receptacle r: receptacles) {
        ids.add(r.getId());
    }

    new_mcd.getReceptacles().clear();

    EntityManager em = Utils.getEntityManager();

    new_mcd.getCountryDestination())

    em.createQuery("UPDATE receptacle r"
            + " SET r.masterCrossDock.id = :mcd_id"
            + " WHERE r.id IN :ids")
    .setParameter("ids", ids)
    .setParameter("mcd_id", new_mcd.getId())
    .executeUpdate();

    new_mcd.getEreturns());
}

Почему я получаю сообщение об ошибке выше и как ее исправить?

Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

Менеджер сущностей не является потокобезопасным.Использование EntityManager в транзакциях, управляемых контейнером, - это нормально, но здесь вы сами управляете и EntityManager, и транзакцией.Кроме того, менеджер сущностей является статическим, поэтому вы эффективно используете его для различных запросов, которые вы можете получить от контроллера.Входящий вызов будет выполнять запрос на обновление, который вызовет сброс.

Я заметил, что во время вашего initEntityManager вы меняете статический экземпляр entityManager на новый.Как насчет старой ссылки, которая может использоваться другим потоком?

Выполните следующие действия:

  1. Удалите полностью ваш метод initEntityManager
  2. Удалите частный статический EntityManager entityManager;
  3. Сделайте ваш метод Utils.getEntityManager ();Для создания нового EntityManager

Альтернативное решение должно заключаться в том, чтобы Spring или ваш контейнер, если вы используете контейнер, управляли своими транзакциями.Создайте сервис, аннотируйте его атрибутом @Transaction и заставьте Spring / Container внедрить в него EntutyManager или просто используйте репозитории spring-data.

0 голосов
/ 16 февраля 2019

Операция flush вызывается реализацией EntityTransaction Hibernate, которая может быть JdbcResourceLocalTransactionCoordinatorImpl в вашем случае на commit.

Внутри SessionImpl, это то, что выбрасывает HibernateException.

private void doFlush() {
    checkTransactionNeeded();
    checkTransactionSynchStatus();

    try {
        if ( persistenceContext.getCascadeLevel() > 0 ) {
            throw new HibernateException( "Flush during cascade is dangerous" );
        }
    ...

Может быть, и я говорю может , какой-то другой поток захватил объект Session и работает с вашими сущностями.

...