简体   繁体   English

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

[英]Hibernate hql, execute multiple update statements in same query

I want to execute multiple update statements in the same query in hibernate Hql. 我想在hibernate Hql中的同一个查询中执行多个更新语句。 like below: 如下:

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

in the same executeUpdate call I want to update records in Table1 and delete records from Table2. 在同一个executeUpdate调用中,我想更新Table1中的记录并从Table2中删除记录。

Is that possible? 那可能吗?

in the same executeUpdate call I want to update records in Table1 and delete records from Table2. 在同一个executeUpdate调用中,我想更新Table1中的记录并从Table2中删除记录。

Is that possible? 那可能吗?

executeUpdate() executes a single update query. executeUpdate()执行单个更新查询。 So, no you cannot do it. 所以,不,你不能这样做。 You have to execute as many update queries as tables to update/delete. 您必须执行与表一样多的更新查询才能更新/删除。 Besides, it makes a code cleaner if you separate queries : 此外,如果您分开查询,它会使代码更清晰:

  • queries are easier to read and parameters setting is readable and not error-prone. 查询更易于阅读,参数设置可读且不容易出错。
  • to debug query execution, it would be easier to understand if queries are handled one by one in their own executeUpdate() 为了调试查询执行,如果在自己的executeUpdate()逐个处理查询,将会更容易理解

It doesn't mean that queries must mandatory be transmited one by one. 这并不意味着必须逐个传输查询。
Batch Processing is a feature provided by Hibernate to improve performance when you want to execute multiples queries. 批处理是Hibernate提供的一项功能,可在您想要执行多个查询时提高性能。 You have to enable the feature to use it. 您必须启用该功能才能使用它。 hibernate.jdbc.batch_size property must be set with a suitable value. 必须使用合适的值设置hibernate.jdbc.batch_size属性。

If you are undertaking batch processing you will need to enable the use of JDBC batching. 如果您正在进行批处理,则需要启用JDBC批处理。 This is absolutely essential if you want to achieve optimal performance. 如果您想获得最佳性能,这绝对是必不可少的。 Set the JDBC batch size to a reasonable number (10-50, for example): 将JDBC批处理大小设置为合理的数字(例如,10-50):

hibernate.jdbc.batch_size 20 Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator. hibernate.jdbc.batch_size 20如果使用身份标识符生成器,​​Hibernate会透明地禁用JDBC级别的插入批处理。

Besides from official documentation : 官方文件外

Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator. 如果您使用身份标识符生成器,​​Hibernate会透明地禁用JDBC级别的插入批处理。

Nevertheless, in your case, it will be useless because as Dragan Bozanovic explained you update/delete different tables in your queries. 然而,在你的情况下,它将是无用的,因为Dragan Bozanovic解释你更新/删除查询中的不同表。 So, it would create as many batch executions as queried tables. 因此,它将创建与查询表一样多的批处理执行。
So, you should execute each query individually. 因此,您应该单独执行每个查询。 Just commit() the transaction when you deem it should be : 只要在你认为应该是:

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

No, it is not possible, because Hibernate uses PreparedStatement s for this (which is good because of bind variables), and PreparedStatement s do not support batches consisting of multiple different statements. 不,这是不可能的,因为Hibernate使用PreparedStatement (由于绑定变量很好),而PreparedStatement不支持由多个不同语句组成的批处理。

PreparedStatement can only batch different combinations of bind variables for one statement, which Hibernate uses for batch inserts/updates when flushing changes in the persistence context (session). PreparedStatement只能为一个语句批量绑定变量的不同组合,Hibernate在刷新持久化上下文(会话)中的更改时用于批量插入/更新

In short, what you are looking is something like batching in JDBC. 简而言之,您所看到的就像在JDBC中进行批处理一样。 Thich is not provided by Hibernate for Bulk Update query, and I doubt if it will ever be considered for Hibernate. Hibernate没有为Bulk Update查询提供Thich,我怀疑它是否会被考虑用于Hibernate。

From my past experience, Batching feature for HQL is rarely useful in real life. 根据我过去的经验,HQL的批处理功能在现实生活中很少有用。 It may sound strange that something being useful in SQL+JDBC but not in HQL. 某些东西在SQL + JDBC中有用但在HQL中却没有用,这听起来很奇怪。 I will try to explain. 我会尽力解释。

Usually when we work with Hibernate (or other similar ORM), we work against entities. 通常当我们使用Hibernate(或其他类似的ORM)时,我们会对实体起作用。 Hibernate will be responsible to synchronize our entities' state with DB, which is most of the cases that JDBC batching can help in improving performance. Hibernate将负责将我们的实体状态与DB同步,这是JDBC批处理可以帮助提高性能的大多数情况。 However, in Hibernate we do not change individual entity's state by Bulk Update query. 但是,在Hibernate中,我们不会通过批量更新查询更改单个实体的状态。

Just give an example, in pseudo-code: 举个例子,用伪代码:

In JDBC, you may do something like (I am trying to mimic what you show in your example): 在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();

Naive translation from JDBC logic to Hibernate may give you something like this: 从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();
    }
}

I suspect you think you need the batching feature because you are doing something similar (based on your example, which you use bulk update for individual record). 我怀疑你认为你需要批处理功能,因为你正在做类似的事情(基于你的例子,你使用批量更新个人记录)。 However it is NOT how thing should be done in Hibernate/JPA 然而,它并不怎么回事应该在休眠/ JPA完成

(Actually it is better to wrap the persistence layer access through a repository, here I am just simplifying the picture) (实际上最好通过存储库包装持久层访问,这里我只是简化了图片)

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

By doing so, Hibernate is intelligent enough to detect changed/deleted/inserted entities, and make use of JDBC batch to do the DB CUD operations at flush() . 通过这样做,Hibernate足够智能地检测已更改/删除/插入的实体,并利用JDBC批处理在flush()执行DB CUD操作。 More important, this is the whole purpose for ORM: we want to provide behavioral-rich entities to work with, for which internal state change of entities can be "transparently" reflected in persistent storage. 更重要的是,这是ORM的全部目的:我们希望提供与行为相关的实体,对于这些实体,实体的内部状态更改可以“透明地”反映在持久存储中。

HQL Bulk Update aims for other usage, which is something like one bulk update to DB to affect a lot of records, eg: 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();

There is seldom need for executing a lot of queries in such kind of usage scenario, therefore, overhead of DB round-trip is insignificant, and hence, benefit for and batch processing support for bulk update query is seldom significant. 在这种使用场景中很少需要执行大量查询,因此,DB往返的开销是微不足道的,因此,对批量更新查询的益处和批处理支持很少是重要的。

I cannot omit that there are cases that you really need to issue a lot of update queries which is not appropriate to be done by meaningful entity behavior. 我不能忽略有些情况下您确实需要发出大量更新查询,这些查询不适合由有意义的实体行为完成。 In such case, you may want to reconsider if Hibernate is the right tool to be used. 在这种情况下,您可能需要重新考虑Hibernate是否是正确使用的工具。 You may consider using pure JDBC in such use case so that you have control on how queries are issued. 您可以考虑在此类用例中使用纯JDBC,以便您可以控制如何发出查询。

The SQL generated by JPA bulk updates/deletes, ie calls to javax.persistence.Query.executeUpdate() cannot be batched by Hibernate when passed through to JDBC. JPA批量更新/删除时生成的SQL,即调用javax.persistence.Query.executeUpdate()在传递给JDBC时不能由Hibernate批处理。 @DraganBozanovic and @AdrianShum have already explained this, but to add to their comments: executeUpdate() returns an int (the number of entities updated or deleted) - irrespective of flushing the Hibernate session, how could the int be returned without calling the database immediately and synchronously? @DraganBozanovic和@AdrianShum已经解释了这一点,但是要添加他们的注释:executeUpdate()返回一个int(更新或删除的实体数) - 无论刷新Hibernate会话,如何在不调用数据库的情况下返回int立即和同步? The JPQL/HQL/SQL would have to be evaluated client-side, which is not possible because the entities to be bulk updated/deleted may not even have been read into the Hibernate session. 必须在客户端评估JPQL / HQL / SQL,这是不可能的,因为要批量更新/删除的实体甚至可能都没有被读入Hibernate会话。 Furthermore if the update/delete were not executed on the database immediately, subsequent queries to read in JPA entities could get stale data. 此外,如果没有立即在数据库上执行更新/删除,则在JPA实体中读取的后续查询可能会获得过时的数据。 Example: 例:

  1. executeUpdate to bulk delete all Customers with ID > 1000. executeUpdate批量删除ID> 1000的所有客户。
  2. read Customer entity with ID = 1001. 读取ID = 1001的客户实体。

If the executeUpdate at 1 were allowed to be deferred until after the read at 2, then you get the wrong answer (Customer still exists). 如果允许将1的executeUpdate推迟到读取2之后,则得到错误的答案(客户仍然存在)。

You either need to read the entities in using JPA, update them, and let Hibernate generate the update SQL (which it can batch), or call JDBC directly to perform batch updates. 您需要使用JPA读取实体,更新它们,让Hibernate生成更新SQL(它可以批量处理),或者直接调用JDBC来执行批量更新。

Why not execute the two queries separately in a Transactional method 为什么不在Transactional方法中单独执行这两个查询

By annotating the method with @Transactional, if any of the query fails, the other won't execute. 通过使用@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();

        ...

 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM