首页 > 解决方案 > hibernate/jpa 抱怨“级联时刷新”

问题描述

我的应用程序因以下错误而崩溃:

org.hibernate.HibernateException: Flush during cascade is dangerous

除非 hibernate 代表我这样做,否则我不会脸红。

眼镜:

这是我的 util 类管理实体管理器的代码:

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());
}

为什么会出现上述错误以及如何解决?

标签: javahibernatejpaflush

解决方案


实体管理器不是线程安全的。在容器管理的事务中使用 EntityManager 很好,但在这里您要自己管理 EntityManager 和事务。此外,实体管理器是静态的,因此您可以在可能从控制器获得的不同请求上有效地重用它。来电将执行更新查询,该查询将调用刷新。

我注意到在您的 initEntityManager 期间,您正在将 entityManager 的静态实例与新实例交换。另一个线程可能正在使用的旧引用呢?

请执行下列操作:

  1. 完全删除您的方法 initEntityManager
  2. 删除私有静态EntityManager entityManager;
  3. 让你方法 Utils.getEntityManager(); 离开创建一个新的EntityManager

如果您使用容器管理事务,替代解决方案应该是让 Spring 或您的容器。创建一个服务,使用 @Transaction 属性对其进行注释并让 Spring/Container 在其中注入 EntutyManager,或者只使用 spring-data 存储库。


推荐阅读