Como a @Version
anotação funciona no JPA?
Encontrei várias respostas cujo extrato é o seguinte:
JPA usa um campo de versão em suas entidades para detectar modificações simultâneas no mesmo registro de armazenamento de dados. Quando o tempo de execução JPA detecta uma tentativa de modificar simultaneamente o mesmo registro, ele lança uma exceção para a última tentativa de confirmação da transação.
Mas ainda não tenho certeza de como funciona.
Também a partir das seguintes linhas:
Você deve considerar os campos de versão imutáveis. A alteração do valor do campo tem resultados indefinidos.
Isso significa que devemos declarar nosso campo de versão como final
?
java
jpa
jpa-annotations
Yatendra Goel
fonte
fonte
UPDATE myentity SET mycolumn = 'new value', version = version + 1 WHERE version = [old.version]
. Se alguém atualizou o registro,old.version
não corresponderá mais ao do banco de dados e a cláusula where impedirá que a atualização aconteça. As 'linhas atualizadas' serão0
, que JPA pode detectar para concluir que uma modificação simultânea aconteceu.Respostas:
Digamos que uma entidade
MyEntity
tenha umaversion
propriedade anotada :Na atualização, o campo anotado com
@Version
será incrementado e adicionado àWHERE
cláusula, algo assim:Se a
WHERE
cláusula não corresponder a um registro (porque a mesma entidade já foi atualizada por outro encadeamento), o provedor de persistência lançará umOptimisticLockException
.Não, mas você pode considerar a possibilidade de tornar o setter protegido como você não deveria chamá-lo.
fonte
long
ou apenaslongValue()
em uma chamada . Você precisa denull
verificações explícitas . Inicializando-o explicitamente por conta própria, eu não faria, pois esse campo deve ser gerenciado pelo provedor ORM. Acho que é definido como0L
na primeira inserção no banco de dados, então se for definido comonull
isso indica que este registro ainda não foi persistido.Long
vez de uma primitivalong
para o@Version
campo, porqueSimpleJpaRepository.save(entity)
provavelmente tratará um campo de versão nula como um indicador de que a entidade é nova. Se a entidade for nova (conforme indicado pelo fato de a versão ser nula), o Spring irá chamarem.persist(entity)
. Mas se a versão tiver um valor, ela será chamadaem.merge(entity)
. É também por isso que um campo de versão declarado como um tipo de wrapper deve ser deixado não inicializado.Embora a resposta @Pascal seja perfeitamente válida, por experiência própria considero o código abaixo útil para realizar o bloqueio otimista:
Por quê? Porque:
@Version
for acidentalmente definido comonull
.optlock
aversion
.O primeiro ponto não importa se o aplicativo usa apenas JPA para inserir dados no banco de dados, pois o fornecedor de JPA aplicará
0
para o@version
campo no momento da criação. Mas quase sempre instruções SQL simples também estão em uso (pelo menos durante os testes de unidade e integração).fonte
u_lmod
(usuário modificado pela última vez), em que o usuário pode ser um processo / entidade / aplicativo humano ou definido (automatizado) (portanto, o armazenamento adicional tambému_lmod_id
pode fazer sentido). (É na minha opinião um metaatributo de negócios muito básico). Normalmente, ele armazena seu valor como DATA (HORA) (significando informações sobre o ano ... milis) em UTC (se a "localização" do fuso horário do editor for importante armazená-la junto com o fuso horário). adicionalmente, muito útil para cenários de sincronização DWH.Cada vez que uma entidade é atualizada no banco de dados, o campo de versão será aumentado em um. Cada operação que atualiza a entidade no banco de dados será anexada
WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
à sua consulta.Ao verificar as linhas afetadas de sua operação, a estrutura jpa pode certificar-se de que não houve modificação simultânea entre carregar e persistir sua entidade, porque a consulta não encontraria sua entidade no banco de dados quando seu número de versão foi aumentado entre carregar e persistir.
fonte
Versão usada para garantir que apenas uma atualização de cada vez. O provedor JPA verificará a versão, se a versão esperada já aumentar, então alguém já atualizou a entidade, então uma exceção será lançada.
Portanto, atualizar o valor da entidade seria mais seguro, mais otimista.
Se o valor muda frequentemente, você pode considerar não usar o campo de versão. Por exemplo, "uma entidade que possui campo contador, que aumentará cada vez que uma página da web for acessada"
fonte
Apenas adicionando um pouco mais de informação.
JPA gerencia a versão por baixo do capô para você, no entanto, não o faz quando você atualiza seu registro via
JPAUpdateClause
; nesses casos, você precisa adicionar manualmente o incremento de versão à consulta.O mesmo pode ser dito sobre a atualização via JPQL, ou seja, não uma simples mudança na entidade, mas um comando de atualização no banco de dados, mesmo que seja feito pelo hibernate
Pedro
fonte
JPAUpdateClause
?