Мое приложение падает с ошибкой ниже:
org.hibernate.HibernateException: Flush during cascade is dangerous
Я не смываю, если спящий режим не делает это от моего имени.
Характеристики:
Это код моего служебного класса для управления диспетчером сущностей:
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());
}
Почему я получаю указанную выше ошибку и как ее исправить?
Операция 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 новым. Как насчет старой ссылки, которая может использоваться другим потоком?
Сделайте следующее:
Альтернативным решением должно быть использование Spring или вашего контейнера, если вы используете контейнер для управления своими транзакциями. Создайте сервис, аннотируйте его атрибутом @Transaction и заставьте Spring/Container внедрить в него EntutyManager или просто используйте репозитории spring-data.
Ах да, он не использует Spring и, возможно, не имеет транзакций, управляемых контейнером. Но все же он может делать то, что я написал в ОБНОВЛЕНИИ. Я думаю, это должно сработать.
Я думаю, что он использует реализацию JAX-RS (см. аннотацию POST), поэтому нет Spring. В любом случае, наличие статического (не финального) EntityManager - это большое нет-нет. В контексте CDI он может быть введен