postgresql - 是否可以让休眠从 postgresql 的值语句生成更新?
问题描述
给定一个 postgresql 表
Table "public.test"
Column | Type | Modifiers
----------+-----------------------------+-----------
id | integer | not null
info | text |
以及以下值:
# select * from test;
id | info
----+--------------
3 | value3
4 | value4
5 | value5
您可能知道使用 postgresql,您可以使用这种语句来更新具有不同值的多行:
update test set info=tmp.info from (values (3,'newvalue3'),(4,'newvalue4'),(5,'newvalue5')) as tmp (id,info) where test.id=tmp.id;
它导致表在单个查询中更新为:
# select * from test;
id | info
----+--------------
3 | newvalue3
4 | newvalue4
5 | newvalue5
我一直在四处寻找如何让hibernate为更新查询生成这种语句。我知道如何使它适用于插入查询(使用 reWriteBatchedInserts jdbc 选项和休眠批处理配置选项)。
但是是否可以进行更新查询或者我必须自己编写本机查询?无论我做什么,hibernate 总是向数据库发送单独的更新查询(我正在查看 postgresql 服务器语句日志以获得此确认)。
2020-06-18 08:19:48.895 UTC [1642] LOG: execute S_6: BEGIN
2020-06-18 08:19:48.895 UTC [1642] LOG: execute S_8: update test set info = $1 where id = $2
2020-06-18 08:19:48.895 UTC [1642] DETAIL: parameters: $1 = 'newvalue3', $2 = '3'
2020-06-18 08:19:48.896 UTC [1642] LOG: execute S_8: update test set info = $1 where id = $2
2020-06-18 08:19:48.896 UTC [1642] DETAIL: parameters: $1 = 'newvalue4', $2 = '4'
2020-06-18 08:19:48.896 UTC [1642] LOG: execute S_8: update test set info = $1 where id = $2
2020-06-18 08:19:48.896 UTC [1642] DETAIL: parameters: $1 = 'newvalue4', $2 = '5'
2020-06-18 08:19:48.896 UTC [1642] LOG: execute S_1: COMMIT
我总是发现发出单个大规模更新查询比针对单行的许多单独更新快很多倍。对于许多单独的更新查询,即使它们是由 jdbc 驱动程序批量发送的,它们仍然需要由服务器顺序处理,因此它不如针对多行的单个更新查询效率高。因此,如果有人有一个不涉及为我的实体编写本机查询的解决方案,我将非常高兴!
更新
为了进一步完善我的问题,我想补充说明。我正在寻找一种不会放弃 Hibernate 脏检查功能以进行实体更新的解决方案。对于必须更新实体列表上具有不同值的几个基本字段的一般情况,我试图避免手动编写批量更新查询。我目前正在研究休眠的 SPI,看看它是否可行。 org.hibernate.engine.jdbc.batch.spi.Batch
似乎是合适的地方,但我还不太确定,因为我从未对休眠 SPI 做过任何事情)。欢迎任何见解!
解决方案
您可以为此使用Blaze-Persistence,它是 JPA 之上的查询构建器,它支持 JPA 模型之上的许多高级 DBMS 功能。
它尚不支持 DML 中的 FROM 子句,但即将在下一个版本中实现:https ://github.com/Blazebit/blaze-persistence/issues/693
同时,您可以为此使用 CTE。首先你需要定义一个 CTE 实体(Blaze-Persistence 的概念):
@CTE
@Entity
public class InfoCte {
@Id Integer id;
String info;
}
我假设您的实体模型看起来大致像这样
@Entity
public class Test {
@Id Integer id;
String info;
}
然后你可以像这样使用 Blaze-Persistence:
criteriaBuilderFactory.update(entityManager, Test.class, "test")
.with(InfoCte.class, false)
.fromValues(Test.class, "newInfos", newInfosCollection)
.bind("id").select("newInfos.id")
.bind("info").select("newInfos.info")
.end()
.set("info")
.from(InfoCte.class, "cte")
.select("cte.info")
.where("cte.id").eqExpression("test.id")
.end()
.whereExists()
.from(InfoCte.class, "cte")
.where("cte.id").eqExpression("test.id")
.end()
.executeUpdate();
这将创建一个类似于以下的 SQL 查询
WITH InfoCte(id, info) AS(
SELECT t.id, t.info
FROM (VALUES(1, 'newValue', ...)) t(id, info)
)
UPDATE test
SET info = (SELECT cte.info FROM InfoCte cte WHERE cte.id = test.id)
WHERE EXISTS (SELECT 1 FROM InfoCte cte WHERE cte.id = test.id)
推荐阅读
- r - ggplot2 轴刻度总是以科学数字出现。我该如何改变它
- r - 如果索引值接近另一个 df 中的索引,有没有办法从 df 中提取数据?
- kubernetes - 无法访问在 RPI 上运行的 K8 集群中的 NGINX nodePort 服务
- c# - 如何获取网址?
- javascript - 在 Nodejs 中使用 Lua 脚本的 Redis mget
- awk - 如何使用 AWK 从文件中读取并输出到 html 表
- react-native - Expo Eject 可在 iOS 上运行,但不能在 Android 上运行
- angular - 如何在 ag-grid cellRendererParams 动作中显示鼠标悬停的文本
- python-3.x - 对分隔值应用查找并重新加入熊猫
- oracle - 使用循环比较 PL/SQL 表中单列的值