Sei que você se preocupa UPDATE
principalmente com o desempenho, mas como colega de manutenção de "ORM", deixe-me dar outra perspectiva sobre o problema de distinguir entre valores "alterados" , "nulos" e "padrão" , que são três coisas diferentes no SQL, mas possivelmente apenas uma coisa no Java e na maioria dos ORMs:
Traduzindo sua justificativa para INSERT
declarações
Seus argumentos a favor da capacidade de armazenamento e cache de instruções são verdadeiros da mesma maneira para INSERT
instruções e para UPDATE
instruções. Mas, no caso de INSERT
declarações, a omissão de uma coluna da declaração tem uma semântica diferente da que em UPDATE
. Significa aplicar DEFAULT
. Os dois seguintes são semanticamente equivalentes:
INSERT INTO t (a, b) VALUES (1, 2);
INSERT INTO t (a, b, c) VALUES (1, 2, DEFAULT);
Isso não é verdade para UPDATE
, onde os dois primeiros são semanticamente equivalentes e o terceiro tem um significado totalmente diferente:
-- 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;
A maioria das APIs de clientes de banco de dados, incluindo JDBC e, consequentemente, JPA, não permite vincular uma DEFAULT
expressão a uma variável de ligação - principalmente porque os servidores também não permitem isso. Se você deseja reutilizar a mesma instrução SQL pelos motivos mencionados de capacidade de manipulação e armazenamento em cache, use a seguinte instrução nos dois casos (supondo que (a, b, c)
todas as colunas estejam t
):
INSERT INTO t (a, b, c) VALUES (?, ?, ?);
E como c
não está definido, você provavelmente ligaria o Java null
à terceira variável de ligação, porque muitos ORMs também não podem distinguir entre NULL
e DEFAULT
( jOOQ , por exemplo, sendo uma exceção aqui). Eles apenas veem Java null
e não sabem se isso significa NULL
(como no valor desconhecido) ou DEFAULT
(como no valor não inicializado).
Em muitos casos, essa distinção não importa, mas, se sua coluna c estiver usando algum dos seguintes recursos, a instrução estará simplesmente errada :
- Tem uma
DEFAULT
cláusula
- Pode ser gerado por um gatilho
Voltar para UPDATE
declarações
Embora o acima seja verdadeiro para todos os bancos de dados, posso garantir que o problema do acionador também é verdadeiro para o banco de dados Oracle. Considere o seguinte 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;
Ao executar o procedimento acima, você verá a seguinte saída:
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
Como você pode ver, a instrução que sempre atualiza todas as colunas sempre aciona o gatilho para todas as colunas, enquanto as instruções que atualizam apenas as colunas que foram alteradas acionam apenas os gatilhos que estão ouvindo essas alterações específicas.
Em outras palavras:
O comportamento atual do Hibernate que você está descrevendo é incompleto e pode até ser considerado errado na presença de gatilhos (e provavelmente outras ferramentas).
Pessoalmente, acho que seu argumento de otimização de cache de consulta é superestimado no caso de SQL dinâmico. Claro, haverá mais algumas consultas nesse cache e um pouco mais de trabalho de análise a ser feito, mas isso geralmente não é um problema para UPDATE
instruções dinâmicas , muito menos do que para SELECT
.
O lote é certamente um problema, mas, na minha opinião, uma única atualização não deve ser normalizada para atualizar todas as colunas apenas porque existe uma pequena possibilidade de a declaração ser recuperável. Provavelmente, o ORM pode coletar sub-lotes de instruções idênticas consecutivas e colocá-las em lote em vez do "lote inteiro" (caso o ORM seja capaz de rastrear a diferença entre "alterado" , "nulo" e "padrão"
UPDATE
é praticamente equivalente a umDELETE
+INSERT
(porque você realmente criar uma nova V ersão da linha). A sobrecarga é alta e aumenta com o número de índices , especialmente se muitas das colunas que as compõem forem realmente atualizadas e a árvore (ou qualquer outra coisa) usada para representar o índice precisar de uma alteração significativa. Não é o número de colunas atualizadas que é relevante, mas se você atualiza uma parte da coluna de um índice.