Wiem, że najbardziej martwisz się UPDATE
głównie wydajnością, ale jako opiekun „ORM” pozwól, że przedstawię Ci inne spojrzenie na problem rozróżnienia między wartościami „zmienionymi” , „zerowymi” i „domyślnymi” , które są trzy różne rzeczy w SQL, ale prawdopodobnie tylko jedna rzecz w Javie i większości ORM:
Tłumaczenie uzasadnienia na INSERT
stwierdzenia
Twoje argumenty na rzecz batchowalności i buforowania instrukcji zachowują się tak samo w przypadku INSERT
instrukcji, jak i w przypadku UPDATE
instrukcji. Ale w przypadku INSERT
instrukcji pominięcie kolumny w instrukcji ma inną semantykę niż w UPDATE
. Oznacza zastosowanie DEFAULT
. Następujące dwa są semantycznie równoważne:
INSERT INTO t (a, b) VALUES (1, 2);
INSERT INTO t (a, b, c) VALUES (1, 2, DEFAULT);
Nie dotyczy to sytuacji UPDATE
, gdy pierwsze dwa są semantycznie równoważne, a trzeci ma zupełnie inne znaczenie:
-- 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;
Większość interfejsów API klienta bazy danych, w tym JDBC, aw konsekwencji JPA, nie pozwala na wiązanie DEFAULT
wyrażenia ze zmienną powiązania - głównie dlatego, że serwery też na to nie pozwalają. Jeśli chcesz ponownie użyć tej samej instrukcji SQL z wyżej wymienionych powodów wsadowości i buforowania instrukcji, możesz użyć następującej instrukcji w obu przypadkach (zakładając, że (a, b, c)
są to wszystkie kolumny t
):
INSERT INTO t (a, b, c) VALUES (?, ?, ?);
A ponieważ c
nie jest ustawiony, prawdopodobnie powiążesz Javę null
z trzecią zmienną powiązania, ponieważ wiele ORM również nie może rozróżnić między NULL
i DEFAULT
( jOOQ , na przykład będący tutaj wyjątkiem). Widzą tylko Javę null
i nie wiedzą, czy to oznacza NULL
(jak w nieznanej wartości) czy DEFAULT
(jak w niezainicjowanej wartości).
W wielu przypadkach to rozróżnienie nie ma znaczenia, ale jeśli kolumna c używa jednej z następujących funkcji, stwierdzenie jest po prostu błędne :
- Ma
DEFAULT
klauzulę
- Może być generowany przez wyzwalacz
Powrót do UPDATE
wyciągów
Chociaż powyższe dotyczy wszystkich baz danych, zapewniam cię, że problem z wyzwalaczem dotyczy również bazy danych Oracle. Rozważ następujący kod 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;
Po uruchomieniu powyższego zobaczysz następujące dane wyjściowe:
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
Jak widać, instrukcja, która zawsze aktualizuje wszystkie kolumny, zawsze wyzwala wyzwalacz dla wszystkich kolumn, natomiast instrukcje aktualizujące tylko kolumny, które uległy zmianie, uruchamiają tylko te wyzwalacze, które oczekują takich konkretnych zmian.
Innymi słowy:
Obecne zachowanie Hibernacji, które opisujesz, jest niekompletne i może nawet zostać uznane za niewłaściwe w obecności wyzwalaczy (i prawdopodobnie innych narzędzi).
Osobiście uważam, że argument optymalizacji pamięci podręcznej zapytania jest przereklamowany w przypadku dynamicznego SQL. Jasne, w takiej pamięci podręcznej będzie jeszcze kilka zapytań i trochę więcej analizowania, ale zwykle nie stanowi to problemu dla UPDATE
instrukcji dynamicznych , a znacznie mniej niż dla SELECT
.
Batching jest z pewnością problemem, ale moim zdaniem pojedyncza aktualizacja nie powinna być znormalizowana, aby zaktualizować wszystkie kolumny tylko dlatego, że istnieje niewielka możliwość, że instrukcja będzie batchowalna. Możliwe, że ORM może zbierać podgrupy kolejnych identycznych instrukcji i grupować je zamiast „całej partii” (w przypadku, gdy ORM jest w stanie nawet śledzić różnicę między „zmienioną” , „zerową” i „domyślną”
UPDATE
jest praktycznie równoznaczne zDELETE
+INSERT
(bo rzeczywiście stworzyć nową V ersion wiersza). Koszty ogólne są wysokie i rosną wraz z liczbą indeksów , zwłaszcza jeśli wiele kolumn, które je zawierają, są faktycznie aktualizowane, a drzewo (lub cokolwiek innego) użyte do reprezentacji indeksu wymaga znacznej zmiany. Istotna jest nie liczba kolumn, które są aktualizowane, ale to, czy aktualizujesz kolumnę jako część indeksu.