ฉันรู้ว่าคุณส่วนใหญ่กังวลUPDATE
และเกี่ยวกับประสิทธิภาพเป็นส่วนใหญ่ แต่ในฐานะผู้ดูแล "ORM" ฉันขอให้คุณอีกมุมมองเกี่ยวกับปัญหาการแยกแยะระหว่างค่า"เปลี่ยน" , "โมฆะ"และ"ค่าเริ่มต้น"ซึ่งเป็น สามสิ่งที่แตกต่างกันใน SQL แต่อาจเป็นเพียงสิ่งเดียวใน Java และใน ORMs ส่วนใหญ่:
การแปลเหตุผลของคุณเป็น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
ไม่ได้ตั้งค่าไว้คุณอาจผูก Java null
กับตัวแปรผูกที่สามเนื่องจาก ORM จำนวนมากยังไม่สามารถแยกแยะความแตกต่างระหว่างNULL
และDEFAULT
( jOOQเช่นเป็นข้อยกเว้นที่นี่) พวกเขาเห็นเฉพาะ Java null
และไม่ทราบว่าสิ่งนี้หมายถึงNULL
(ตามค่าที่ไม่รู้จัก) หรือDEFAULT
(ตามค่าเริ่มต้น)
ในหลายกรณีความแตกต่างนี้ไม่สำคัญ แต่ในกรณีที่คอลัมน์ของคุณใช้คุณสมบัติใด ๆ ต่อไปนี้ข้อความสั่งนั้นเรียบง่าย ผิดปกติ :
- มันมี
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
SELECT
การแบตช์นั้นเป็นปัญหา แต่ในความเห็นของฉันการอัพเดทครั้งเดียวไม่ควรทำให้เป็นมาตรฐานเพื่ออัพเดทคอลัมน์ทั้งหมดเพียงเพราะมีความเป็นไปได้เล็กน้อยที่คำสั่งนั้นจะสามารถทำการแบตช์ได้ โอกาสคือ ORM สามารถรวบรวมแบทช์ย่อยของชุดคำสั่งที่เหมือนกันติดต่อกันและแบทช์แทนที่จะเป็น "ทั้งชุด" (ในกรณีที่ ORM สามารถติดตามความแตกต่างระหว่าง"เปลี่ยน" , "โมฆะ"และ"ค่าเริ่มต้น" ได้)
UPDATE
จะมีค่าเท่ากับDELETE
+INSERT
(เพราะคุณสร้างV ersion ใหม่ของแถว) ค่าใช้จ่ายสูงและเติบโตตามจำนวนดัชนีโดยเฉพาะอย่างยิ่งหากมีคอลัมน์จำนวนมากที่ประกอบด้วยพวกเขาได้รับการปรับปรุงจริง ๆ และต้นไม้ (หรืออะไรก็ตาม) ที่ใช้แทนดัชนีจำเป็นต้องมีการเปลี่ยนแปลงที่สำคัญ ไม่ใช่จำนวนคอลัมน์ที่อัปเดตสิ่งที่เกี่ยวข้อง แต่ไม่ว่าคุณจะอัปเดตส่วนคอลัมน์ของดัชนีหรือไม่