Tôi biết bạn chủ yếu quan tâm UPDATE
và chủ yếu về hiệu suất, nhưng với tư cách là người duy trì "ORM" đồng nghiệp, hãy để tôi cung cấp cho bạn một góc nhìn khác về vấn đề phân biệt giữa các giá trị "thay đổi" , "null" và "mặc định" , đó là ba điều khác nhau trong SQL, nhưng có thể chỉ có một điều trong Java và trong hầu hết các ORM:
Dịch lý do của bạn để INSERT
báo cáo
Các đối số của bạn có lợi cho tính linh hoạt và khả năng lưu trữ câu lệnh giữ đúng theo cách tương tự đối với các INSERT
câu lệnh như chúng làm cho các UPDATE
câu lệnh. Nhưng trong trường hợp các INSERT
câu lệnh, bỏ qua một cột trong câu lệnh có một ngữ nghĩa khác với trong UPDATE
. Nó có nghĩa là để áp dụng DEFAULT
. Hai cái sau tương đương về mặt ngữ nghĩa:
INSERT INTO t (a, b) VALUES (1, 2);
INSERT INTO t (a, b, c) VALUES (1, 2, DEFAULT);
Điều này không đúng với UPDATE
, trong đó hai cái đầu tiên tương đương về mặt ngữ nghĩa và cái thứ ba có ý nghĩa hoàn toàn khác:
-- 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;
Hầu hết các API máy khách cơ sở dữ liệu, bao gồm JDBC và do đó, JPA, không cho phép liên kết một DEFAULT
biểu thức với biến liên kết - chủ yếu là do các máy chủ không cho phép điều này. Nếu bạn muốn sử dụng lại cùng một câu lệnh SQL cho các lý do khả năng lưu trữ hàng loạt và khả năng lưu lại câu lệnh đã nói ở trên, bạn sẽ sử dụng câu lệnh sau trong cả hai trường hợp (giả sử (a, b, c)
là tất cả các cột trong t
):
INSERT INTO t (a, b, c) VALUES (?, ?, ?);
Và vì c
không được đặt, nên bạn có thể liên kết Java null
với biến liên kết thứ ba, vì nhiều ORM cũng không thể phân biệt giữa NULL
và DEFAULT
( jOOQ , ví dụ như là một ngoại lệ ở đây). Họ chỉ nhìn thấy Java null
và không biết điều này có nghĩa NULL
(như trong giá trị không xác định) hoặc DEFAULT
(như trong giá trị chưa được khởi tạo).
Trong nhiều trường hợp, sự khác biệt này không thành vấn đề, nhưng trong trường hợp cột c của bạn đang sử dụng bất kỳ tính năng nào sau đây, thì tuyên bố đơn giản là sai :
- Nó có một
DEFAULT
điều khoản
- Nó có thể được tạo ra bởi một kích hoạt
Quay lại UPDATE
báo cáo
Mặc dù những điều trên là đúng với tất cả các cơ sở dữ liệu, tôi có thể đảm bảo với bạn rằng vấn đề kích hoạt cũng đúng với cơ sở dữ liệu Oracle. Hãy xem xét SQL sau:
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;
Khi bạn chạy ở trên, bạn sẽ thấy đầu ra sau:
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
Như bạn có thể thấy, câu lệnh luôn cập nhật tất cả các cột sẽ luôn kích hoạt trình kích hoạt cho tất cả các cột, trong khi các câu lệnh chỉ cập nhật các cột đã thay đổi sẽ chỉ kích hoạt những trình kích hoạt đang lắng nghe những thay đổi cụ thể đó.
Nói cách khác:
Hành vi hiện tại của Hibernate mà bạn mô tả là không đầy đủ và thậm chí có thể bị coi là sai khi có trình kích hoạt (và có thể là các công cụ khác).
Cá nhân tôi nghĩ rằng đối số tối ưu hóa bộ đệm truy vấn của bạn được đánh giá cao trong trường hợp SQL động. Chắc chắn, sẽ có thêm một vài truy vấn trong bộ đệm như vậy và một số công việc phân tích cú pháp sẽ được thực hiện, nhưng điều này thường không phải là vấn đề đối với các UPDATE
câu lệnh động , ít hơn nhiều so với SELECT
.
Batching chắc chắn là một vấn đề, nhưng theo tôi, một bản cập nhật duy nhất không nên được chuẩn hóa để cập nhật tất cả các cột chỉ vì có một khả năng nhỏ là câu lệnh có thể được bó. Rất có thể, ORM có thể thu thập các lô con của các câu lệnh giống hệt nhau liên tiếp và các đợt thay vì "toàn bộ lô" (trong trường hợp ORM thậm chí có khả năng theo dõi sự khác biệt giữa "thay đổi" , "null" và "mặc định"
UPDATE
thì thực tế tương đương với dấuDELETE
+INSERT
(vì bạn thực sự tạo ra một chữ V mới của hàng). Chi phí hoạt động cao và tăng theo số lượng chỉ mục , đặc biệt nếu nhiều cột bao gồm chúng được cập nhật thực sự và cây (hoặc bất cứ thứ gì) được sử dụng để thể hiện chỉ mục cần một thay đổi đáng kể. Đây không phải là số lượng cột được cập nhật những gì có liên quan, nhưng liệu bạn có cập nhật một phần cột của chỉ mục hay không.