Я знаю, что вы в основном озабочены 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 в ряду). Издержки высоки и растут с увеличением количества индексов , особенно если многие столбцы, которые их содержат, фактически обновлены, а дерево (или что-то еще), используемое для представления индекса, нуждается в значительном изменении. Важно не количество столбцов, которые обновляются, а то, обновляете ли вы часть столбца индекса.