Я знаю, что вы в основном озабочены UPDATEи в основном производительностью, но как сотрудник службы поддержки «ORM», позвольте мне дать вам еще один взгляд на проблему различия между «измененными» , «нулевыми» и «значениями по умолчанию» , которые три разные вещи в SQL, но, возможно, только одна вещь в Java и в большинстве ORM:
Перевод вашего обоснования в 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(как в неинициализированном значении).
Во многих случаях это различие не имеет значения, но в случае, если ваш столбец c использует любую из следующих функций, утверждение просто неверно :
- Есть
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 в ряду). Издержки высоки и растут с увеличением количества индексов , особенно если многие столбцы, которые их содержат, фактически обновлены, а дерево (или что-то еще), используемое для представления индекса, нуждается в значительном изменении. Важно не количество столбцов, которые обновляются, а то, обновляете ли вы часть столбца индекса.