Saya tahu Anda sebagian besar mengkhawatirkan UPDATE
dan 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 INSERT
pernyataan
Argumen Anda yang mendukung batchability dan pernyataan cacheability berlaku dengan cara yang sama untuk INSERT
pernyataan seperti yang mereka lakukan untuk UPDATE
pernyataan. Tetapi dalam kasus INSERT
pernyataan, 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 DEFAULT
ekspresi 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 c
tidak disetel, Anda mungkin akan mengikat Java null
ke variabel mengikat ketiga, karena banyak ORM juga tidak dapat membedakan antara NULL
dan DEFAULT
( jOOQ , misalnya menjadi pengecualian di sini). Mereka hanya melihat Java null
dan 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
DEFAULT
klausa
- Mungkin dihasilkan oleh pemicu
Kembali ke UPDATE
pernyataan
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 UPDATE
pernyataan 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"
UPDATE
praktis 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.