Hibernate/jpa жалуется на «сброс во время каскада»

Мое приложение падает с ошибкой ниже:

org.hibernate.HibernateException: Flush during cascade is dangerous

Я не смываю, если спящий режим не делает это от моего имени.

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

  • веб-приложение на коте
  • 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());
}

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
1 467
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Операция 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 и работает с вашими сущностями.

Ответ принят как подходящий

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

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

Сделайте следующее:

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

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

Я думаю, что он использует реализацию JAX-RS (см. аннотацию POST), поэтому нет Spring. В любом случае, наличие статического (не финального) EntityManager - это большое нет-нет. В контексте CDI он может быть введен

LppEdd 16.02.2019 13:34

Ах да, он не использует Spring и, возможно, не имеет транзакций, управляемых контейнером. Но все же он может делать то, что я написал в ОБНОВЛЕНИИ. Я думаю, это должно сработать.

Alexander Petrov 16.02.2019 13:36

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

Как моделировать таблицы отношений как сущности в Hibernate
Spring Boot: Hibernate продолжает вызывать сложные запросы внешнего соединения
У меня есть ошибка с спящим режимом: «не удалось получить значение поля с помощью метода получения отражения»
Как реализовать дао, чтобы получить список без createCriteria() и получить список со следующими свойствами?
Как Tomcat устанавливает загрузчик класса контекста для потоков, обрабатывающих HTTP-запрос?
Почему проекция Hibernate Criteria возвращает нулевой объект?
Одна и та же последовательность для идентификаторов в нескольких объектах
Неверный идентификатор свойства класса bean-компонента. Не удалось найти поле для свойства во время резервного доступа
Получение исключения MysqlDataTruncation при попытке сохранить дату, которая была инициализирована аргументом миллисекунды
Hibernate Mapping Не удалось извлечь ResultSet