【问题标题】:hibernate/jpa complaining "flush during cascade"hibernate/jpa 抱怨“级联时刷新”
【发布时间】:2019-07-10 07:51:48
【问题描述】:

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

org.hibernate.HibernateException: Flush during cascade is dangerous

除非 hibernate 代表我这样做,否则我不会刷新。

规格:

  • tomcat 上的 webapp
  • hibernate/jpa 用于持久性(应用程序管理 实体经理)

这是我的 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());
}

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

【问题讨论】:

    标签: java hibernate jpa flush


    【解决方案1】:

    实体管理器不是线程安全的。在容器管理的事务中使用 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 上下文中,它可以被注入
    • 哦,是的,他不使用 spring,并且可能没有容器管理的事务。但他仍然可以做我在更新中写的。我认为它应该可以工作。
    【解决方案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 对象并正在对您的实体进行操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-23
      • 1970-01-01
      • 2014-11-16
      相关资源
      最近更新 更多