Saya tahu Anda sebagian besar mengkhawatirkan UPDATEdan sebagian besar tentang kinerja, tetapi sebagai sesama pengelola "ORM", izinkan saya memberi Anda perspektif lain tentang masalah membedakan antara nilai "berubah" , "nol" , dan "default" , yang merupakan tiga hal berbeda dalam SQL, tetapi mungkin hanya satu hal di Java dan di sebagian besar ORM:
Menerjemahkan alasan Anda ke INSERTpernyataan
Argumen Anda yang mendukung batchability dan pernyataan cacheability berlaku dengan cara yang sama untuk INSERTpernyataan seperti yang mereka lakukan untuk UPDATEpernyataan. Tetapi dalam kasus INSERTpernyataan, menghilangkan kolom dari pernyataan memiliki semantik yang berbeda dari pada UPDATE. Itu artinya melamar DEFAULT. Dua berikut ini setara secara semantik:
INSERT INTO t (a, b) VALUES (1, 2);
INSERT INTO t (a, b, c) VALUES (1, 2, DEFAULT);
Ini tidak benar untuk UPDATE, di mana dua yang pertama secara semantik setara, dan yang ketiga memiliki makna yang sama sekali berbeda:
-- 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;
Sebagian besar API klien basis data, termasuk JDBC, dan akibatnya, JPA, tidak memungkinkan untuk mengikat DEFAULTekspresi ke variabel terikat - sebagian besar karena server juga tidak mengizinkan ini. Jika Anda ingin menggunakan kembali pernyataan SQL yang sama untuk alasan batchability dan cacheability pernyataan tersebut di atas, Anda akan menggunakan pernyataan berikut dalam kedua kasus (dengan asumsi (a, b, c)semua kolom di t):
INSERT INTO t (a, b, c) VALUES (?, ?, ?);
Dan karena ctidak disetel, Anda mungkin akan mengikat Java nullke variabel mengikat ketiga, karena banyak ORM juga tidak dapat membedakan antara NULLdan DEFAULT( jOOQ , misalnya menjadi pengecualian di sini). Mereka hanya melihat Java nulldan tidak tahu apakah ini artinyaNULL (seperti dalam nilai yang tidak diketahui) atau DEFAULT(seperti dalam nilai yang tidak diinisialisasi).
Dalam banyak kasus, perbedaan ini tidak masalah, tetapi dalam kasus kolom Anda c menggunakan salah satu dari fitur berikut, pernyataan itu salah :
- Ada
DEFAULTklausa
- Mungkin dihasilkan oleh pemicu
Kembali ke UPDATEpernyataan
Sementara hal di atas berlaku untuk semua database, saya dapat meyakinkan Anda bahwa masalah pemicunya juga berlaku untuk database Oracle. Pertimbangkan SQL berikut:
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;
Ketika Anda menjalankan di atas, Anda akan melihat output berikut:
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
Seperti yang Anda lihat, pernyataan yang selalu memperbarui semua kolom akan selalu memicu pemicu untuk semua kolom, sedangkan pernyataan yang memperbarui hanya kolom yang berubah akan memecat hanya pemicu yang mendengarkan perubahan spesifik tersebut.
Dengan kata lain:
Perilaku Hibernate saat ini yang Anda gambarkan tidak lengkap dan bahkan dapat dianggap salah di hadapan pemicu (dan mungkin alat lain).
Saya pribadi berpikir bahwa argumen optimisasi cache kueri Anda dinilai terlalu tinggi dalam kasus SQL dinamis. Tentu, akan ada beberapa pertanyaan lagi di cache seperti itu, dan sedikit pekerjaan parsing yang harus dilakukan, tetapi ini biasanya bukan masalah untuk UPDATEpernyataan dinamis , apalagi untuk SELECT.
Batching tentu saja merupakan masalah, tetapi menurut pendapat saya, satu pembaruan tidak seharusnya dinormalisasi untuk memperbarui semua kolom hanya karena ada sedikit kemungkinan pernyataan tersebut dapat dikelompokkan. Kemungkinannya adalah, ORM dapat mengumpulkan sub-batch dari pernyataan identik yang berurutan dan mengelompokkannya sebagai ganti dari "seluruh batch" (jika ORM bahkan mampu melacak perbedaan antara "berubah" , "null" , dan "default"
UPDATEpraktis setara denganDELETE+INSERT(karena Anda benar-benar membuat yang baru V ersion baris). Overheadnya tinggi, dan tumbuh dengan jumlah indeks , khususnya jika banyak kolom yang menyusunnya benar-benar diperbarui, dan pohon (atau apa pun) yang digunakan untuk mewakili indeks memerlukan perubahan yang signifikan. Itu bukan jumlah kolom yang diperbarui yang relevan, tetapi apakah Anda memperbarui bagian kolom dari indeks.