我知道您最关心UPDATE
并且主要关注性能,但是作为“ ORM”维护者,让我对区分“ changed”,“ null”和“ default”值的问题有另一种看法。SQL中有三件事,但在Java和大多数ORM中可能只有一件事:
将您的理由转化为INSERT
陈述
支持批处理和语句可缓存性的论点对于INSERT
语句和对UPDATE
语句的处理方式相同。但是对于INSERT
语句,从语句中省略列的语义与中的不同UPDATE
。这意味着申请DEFAULT
。以下两个在语义上是等效的:
INSERT INTO t (a, b) VALUES (1, 2);
INSERT INTO t (a, b, c) VALUES (1, 2, DEFAULT);
对于UPDATE
,情况并非如此,其中前两个在语义上是等效的,而第三个具有完全不同的含义:
-- These are the same
UPDATE t SET a = 1, b = 2;
UPDATE t SET a = 1, b = 2, c = c;
-- This is different!
UPDATE t SET a = 1, b = 2, c = DEFAULT;
大多数数据库客户端API(包括JDBC,因此也包括JPA)都不允许将DEFAULT
表达式绑定到绑定变量-主要是因为服务器也不允许这样做。如果您出于上述可批处理性和语句可缓存性的原因想要重复使用同一条SQL语句,则在两种情况下(a, b, c)
都应使用以下语句(假定中的所有列t
):
INSERT INTO t (a, b, c) VALUES (?, ?, ?);
而且由于c
未设置,您可能null
会将Java绑定到第三个绑定变量,因为许多ORM也无法区分NULL
和DEFAULT
(jOOQ,例如在这里是一个例外)。他们只看到Java null
,不知道这是否意味着NULL
(如未知值)还是DEFAULT
(如未初始化的值)。
在很多情况下,这种区别并不重要,但是如果您的c列使用了以下任何功能,则该语句完全是错误的:
- 它有一个
DEFAULT
子句
- 它可能是由触发器生成的
回到 UPDATE
声明
尽管以上内容适用于所有数据库,但我可以向您保证触发器问题也适用于Oracle数据库。考虑以下SQL:
CREATE TABLE x (a INT PRIMARY KEY, b INT, c INT, d INT);
INSERT INTO x VALUES (1, 1, 1, 1);
CREATE OR REPLACE TRIGGER t
BEFORE UPDATE OF c, d
ON x
BEGIN
IF updating('c') THEN
dbms_output.put_line('Updating c');
END IF;
IF updating('d') THEN
dbms_output.put_line('Updating d');
END IF;
END;
/
SET SERVEROUTPUT ON
UPDATE x SET b = 1 WHERE a = 1;
UPDATE x SET c = 1 WHERE a = 1;
UPDATE x SET d = 1 WHERE a = 1;
UPDATE x SET b = 1, c = 1, d = 1 WHERE a = 1;
运行以上命令时,将看到以下输出:
table X created.
1 rows inserted.
TRIGGER T compiled
1 rows updated.
1 rows updated.
Updating c
1 rows updated.
Updating d
1 rows updated.
Updating c
Updating d
如您所见,始终更新所有列的语句将始终触发所有列的触发器,而仅更新已更改的列的语句将仅触发那些正在监听此类特定更改的触发器。
换一种说法:
您正在描述的Hibernate当前行为是不完整的,甚至在存在触发器(可能还有其他工具)的情况下,甚至可能被认为是错误的。
我个人认为,对于动态SQL,您的查询缓存优化参数被高估了。当然,在这样的缓存中将有更多的查询,并且还有更多的解析工作要做,但是对于动态UPDATE
语句来说,这通常不是问题,比for少得多SELECT
。
批处理当然是一个问题,但是我认为,不应单单更新一次以更新所有列,因为仅仅存在语句可批处理的可能性。可能的是,ORM可以收集连续相同语句的子批,然后对那些子批进行批处理,而不是“整个批”(如果ORM甚至能够跟踪“ changed”,“ null”和“ default”之间的差异)
UPDATE
是几乎等同于一个DELETE
+INSERT
(因为你实际上是创建一个新的V行的版为)。开销很高,并且随着索引数量的增加而增加,特别是如果组成它们的许多列实际上已更新,并且 用于表示索引的树(或其他任何东西)需要进行重大更改时,尤其如此。重要的不是要更新的列数,而是是否更新索引的列部分。