Hibernate hql,在同一个查询中执行多个更新语句

时间:2016-08-19 07:57:50

标签: java mysql hibernate hql

我想在hibernate Hql中的同一个查询中执行多个更新语句。 如下所示:

$scope.result = []
data.forEach(function(d){
    $scope.result.push({"id":d.id, "text":d.text});
});

在同一个hql = " update Table1 set prob1=null where id=:id1; " + " delete from Table2 where id =:id2 "; ... query.executeUpdate(); 调用中我想更新Table1中的记录并从Table2中删除记录。

这可能吗?

5 个答案:

答案 0 :(得分:4)

  

在同一个executeUpdate调用中我想更新Table1和中的记录   从表2中删除记录。

     

这可能吗?

executeUpdate()执行单个更新查询。 所以,不,你不能这样做。您必须执行与表一样多的更新查询才能更新/删除。 此外,如果您分开查询,它会使代码更清晰:

  • 查询更易于阅读,参数设置可读且不容易出错。
  • 调试查询执行,如果在自己的executeUpdate()
  • 中逐个处理查询,将会更容易理解

这并不意味着必须逐个传递查询 批处理是Hibernate提供的一项功能,可在您想要执行多个查询时提高性能。您必须启用该功能才能使用它。 必须使用合适的值设置hibernate.jdbc.batch_size属性。

  

如果您正在进行批处理,则需要启用批处理   使用JDBC批处理。如果你愿意,这绝对是必不可少的   实现最佳性能。将JDBC批处理大小设置为合理   数字(例如10-50):

     

hibernate.jdbc.batch_size 20 Hibernate禁用插入批处理   如果使用标识符生成器,​​则透明地使用JDBC级别。

除了official documentation

  

如果,Hibernate会透明地禁用JDBC级别的插入批处理   您使用身份标识符生成器。

然而,在你的情况下,它将是无用的,因为Dragan Bozanovic解释你更新/删除查询中的不同表。因此,它将创建与查询表一样多的批处理执行 因此,您应该单独执行每个查询。当你认为应该是:(/ p>)时,只需提交()

hql = "update Table1 set prob1=null where id=:id1;"
...
query.setParameter("id1",...);
query.executeUpdate();
hql = "delete from Table2 where id =:id2";
...
query.executeUpdate();
query.setParameter("id2",...);
..
tx.commit();

答案 1 :(得分:3)

不,这是不可能的,因为Hibernate为此使用了PreparedStatement(由于绑定变量这很好),PreparedStatement不支持由多个不同语句组成的批处理。

PreparedStatement只能为一个语句批量绑定变量的不同组合,Hibernate在刷新持久化上下文(会话)中的更改时用于batch inserts/updates

答案 2 :(得分:3)

简而言之,您所看到的就像在JDBC中批处理一样。 Hibernate没有为批量更新查询提供Thich,我怀疑它是否会被考虑用于Hibernate。

根据我过去的经验,HQL的批处理功能在现实生活中很少有用。某些东西在SQL + JDBC中有用但在HQL中却没有用,这听起来很奇怪。我会尝试解释。

通常当我们使用Hibernate(或其他类似的ORM)时,我们会对实体起作用。 Hibernate将负责将我们的实体状态与DB同步,这是JDBC批处理可以帮助提高性能的大多数情况。但是,在Hibernate中,我们不会通过批量更新查询更改单个实体的状态。

举个例子,用伪代码:

在JDBC中,您可以执行类似的操作(我试图模仿您在示例中显示的内容):

List<Order> orders = findOrderByUserId(userName);
for (Order order: orders) {
    if (order outstanding quantity is 0) {
        dbConn.addBatch("update ORDER set STATE='C' where ID=:id", order.id);
    } else if (order is after expriation time) {
        dbConn.addBatch("delete ORDER where ID=:id", order.id);
    }
}
dbConn.executeBatch();

从JDBC逻辑到Hibernate的简单翻译可能会给你这样的东西:

List<Order> orders = findOrderByUserId(userName);
for (Order order: orders) {
    if (order outstanding quantity is 0) {
        q = session.createQuery("update Order set state='C' where id=:id");
        q.setParameter("id", order.id);
        q.executeUpdate();
    } else if (order is after expriation time) {
        q = session.createQuery("delete Order where id=:id");
        q.setParameter("id", order.id);
        q.executeUpdate();
    }
}

我怀疑您认为您需要批处理功能,因为您正在执行类似的操作(基于您的示例,您使用批量更新进行单个记录)。但是 NOT 应该如何在Hibernate / JPA

中完成

(实际上最好通过存储库包装持久层访问,这里我只是简化了图片)

List<Order> orders = findOrderByUserId(userName);
for (Order order: orders) {
    if (order.anyOutstanding()) {
        order.complete();    // which internally update the state
    } else if (order.expired) {
        session.delete(order);
    }
}

session.flush();   // or you may simply leave it to flush automatically before txn commit

通过这样做,Hibernate足够智能,可以检测已更改/删除/插入的实体,并利用JDBC批处理在flush()进行DB CUD操作。更重要的是,这是ORM的全部目的:我们希望提供与行为相关的实体,实体的内部状态变化可以“透明地”反映在持久存储中。

HQL批量更新旨在用于其他用途,例如对DB进行批量更新以影响大量记录,例如:

q = session.createQuery("update Order set state='C' " 
                        + " where user.id=:user_id "
                        + " and outstandingQty = 0 and state != 'C' ");
q.setParameter("user_id", userId);
q.executeUpdate();

在这种使用场景中很少需要执行大量查询,因此,DB往返的开销微不足道,因此,批量更新查询的好处和批量处理支持很少发生。

我不能忽略有些情况下您确实需要发出大量更新查询,这些查询不适合由有意义的实体行为完成。在这种情况下,您可能需要重新考虑Hibernate是否是正确使用的工具。您可以考虑在此类用例中使用纯JDBC,以便您可以控制如何发出查询。

答案 3 :(得分:1)

由JPA批量更新/删除生成的SQL,即对javax.persistence.Query.executeUpdate()的调用在传递给JDBC时不能由Hibernate批处理。 @DraganBozanovic和@AdrianShum已经解释了这一点,但是要添加他们的注释:executeUpdate()返回一个int(更新或删除的实体数) - 无论刷新Hibernate会话,如何在不调用数据库的情况下返回int立即和同步?必须在客户端评估JPQL / HQL / SQL,这是不可能的,因为要批量更新/删除的实体甚至可能都没有被读入Hibernate会话。此外,如果未立即在数据库上执行更新/删除,则在JPA实体中读取的后续查询可能会获得过时数据。例如:

  1. executeUpdate批量删除ID为&gt;的所有客户1000。
  2. 读取ID = 1001的客户实体。
  3. 如果允许将1的executeUpdate推迟到读取2之后,那么你得到错误的答案(客户仍然存在)。

    您需要阅读使用JPA的实体,更新它们,让Hibernate生成更新SQL(它可以批处理),或者直接调用JDBC来执行批量更新。

答案 4 :(得分:0)

为什么不在Transactional方法中单独执行两个查询

通过使用@Transactional注释方法,如果任何查询失败,则另一个不会执行。

 @Transactional(propagation = Propagation.REQUIRED, readOnly = false)

 public void executeQuery(Obj1 obj1) {

 String query="update table1 set actualRepaymentAmount=expectedRepaymentAmount,active='Y'  where loanCaseId = '"+caseId+"'";
        sessionFactory.getCurrentSession().createQuery(query).executeUpdate();

 query="update table2 set loanStatus='C' where loanCaseId = '"+caseId+"'";  
    sessionFactory.getCurrentSession().createQuery(query).executeUpdate();

        ...

 }