An Interest In:
Web News this Week
- April 2, 2024
- April 1, 2024
- March 31, 2024
- March 30, 2024
- March 29, 2024
- March 28, 2024
- March 27, 2024
Criando Sistemas de Reservas Com Versionless Optmistic Locking, Spring Boot e JPA/Hibernate
Como visto no artigo anterior o Optmistic Locking uma opo para lidar com Race Conditions para cenrios com baixo e/ou moderado ndice de conflitos. O principal motivo para o uso de bloqueio otimista nestes cenrios porque no haver um nmero exagerado de transaes conflitantes e o seu banco de dados (BD) no ser sobrecarregado com uma alta quantidade de rollbacks.
Caso seu sistema necessite atualizar atributos distintos de uma entidade simultaneamente o Optmistic Locking poder se tornar um empecilho dado que o mecanismo de bloqueio otimista encara as atualizaes como tudo ou nada, ou seja, caso a verso da entidade em memria no corresponda a presente ao (BD) a transao ser abortada independente se os campos atualizados no sejam sobrepostos.
Para entender melhor, dado a entidade Mesa descrita no diagrama abaixo.
Imagine que duas transaes simultneas desejem atualizar os atributos quantidadeDeLugares
e disponivelParaReserva
. Ambas consultem o registro e recebam a mesma verso da entidade Mesa
. A primeira transao (Tx1) atualiza a quantidade de lugares, fazendo com que a verso da mesa seja incrementada. Enquanto a segunda transao (Tx2) tenta atualizar o status da mesa para indisponvel para reserva, porm, dado que Tx1 incrementou a verso da entidade, agora a verso est obsoleta, portanto, Tx2 sera abortada.
Em casos de uso onde necessrio que atualizaes com atributos no sobrepostos no conflitem, ou quando alterar o schema de dados para inserir um atributo de verso seja caro independente do motivo o Hibernate oferece suporte ao controle de concorrncia com mecanismo de Versionless Optmistic Locking ou bloqueio otimista sem verso em portugus.
Version-less optimistic locking
Os bloqueios otimistas esto relacionados a um atributo incremental para controle de verso da entidade, e atravs deste que a integridade e consistncia so mantidas em atualizaes concorrentes.
Existem sistemas onde aplicar o custo para utilizar uma estratgia de Optimistic Locking alto, pois no simples inserir uma nova coluna na tabela dado que o impacto desta mudana poder causar anomalias em outras funcionalidades do sistema, ou para realizar mudanas no schema exija aprovaes em um sistema altamente burocrtico, ou voc trabalha em um sistema legado onde alterar o schema no uma opo.
Para estes casos possvel criar um versionamento da entidade apatir do prprio estado do recurso, seja atravs de uma coluna atualizada ou da juno de todas colunas.
Versionless Optmistic Locking com Hibernate
Hibernate oferece suporte ao controle de concorrncia com bloqueio otimista sem verso, para habilitar o mesmo devemos utilizar anotao @OptimisticLocking
ao nvel de entidade, esta anotao recebe como argumento a estratgia de bloqueio otimista descrita por um OptimisticLockType
.
O Hibernate oferece suporte as seguintes estratgias de bloqueio otimista.
OptimisticLockType.NONE
: indica que o mecanismo de bloqueio otimista est desabilitado;OptimisticLockType.VERSION
: indica que o mecanismo de bloqueio otimista utilizar o atributo anotado com@Version
como verso da entidade;OptimisticLockType.DIRTY
: indica que o mecanismo de bloqueio otimista utilizar apenas os atributos atualizados pela transao como verso da entidade;OptimisticLockType.ALL
: indica que o mecanismo de bloqueio otimista utilizar todos os atributos da entidade como verso;
Como o mecanismo de controle de concorrncia precisa inserir condies adicionais em sua Query
de atualizao, necessrio que a entidade seja anotado com @DynamicUpdate
ao nvel de entidade.
@Entity@OptimisticLocking(type = OptimisticLockType.ALL)@DynamicUpdatepublic class Mesa { @Id @GeneratedValue private Long id; //Demais campos, construtores e metodos omitidos }
Controle de Concorrncia com OptimisticLockType.ALL
Dado que a estratgia de bloqueio otimista OptimisticLockType.ALL
esteja habilitada no momento de uma atualizao o Hibernate ir verificar se o estado de cada atributo da entidade corresponde em memria ao estado presente no BD.
Para melhor entendimento observe o cdigo abaixo.
@Transactionalpublic void atualizarQuantidadeDeLugares(Long mesaId, int qtdLugares){ Mesa mesa = entityManager.find(Mesa.class, mesaId); mesa.setQuatidadeDeLugares(qtdLugares);}
Hibernate gera o seguinte SQL:
SELECT * FROM mesa m WHERE m.id = :mesaId UPDATE mesa SET quantidadeLugares = :qtdLugares WHERE id = :medaId AND quantidadeLugares = 2 AND disponivelParaReserva = true
No SQL acima possvel visualizar que todos os campos se uniram e convergiram atributo de verso global. Esta estratgia utilizada quando impossvel inserir um atributo para versionamento da entidade. Em contrapartida, as atualizaes com atributos no sobrepostos conflitam.
Controle de Concorrncia com OptimisticLockType.DIRTY
A estratgia de bloqueio otimista OptimisticLockType.DIRTY
favorece que apenas os campos "sujos" na atualizao sejam utilizados como verso da entidade, ou seja, agora atualizaes com atributos no sobrepostos no conflitam mais.
Observe o comportamento do seguinte mtodo de atualizao.
@Transactionalpublic void atualizarQuantidadeDeLugares(Long mesaId, int qtdLugares){ Mesa mesa = entityManager.find(Mesa.class, mesaId); mesa.setQuatidadeDeLugares(qtdLugares);}
Hibernate gera o seguinte SQL:
SELECT * FROM mesa m WHERE m.id = :mesaId UPDATE mesa SET quantidadeLugares = :qtdLugares WHERE id = :medaId AND quantidadeLugares = 2
O SQL apresentado acima demostra que apenas o atributo quantidadeLugares
foi utilizado na condio do WHERE
, ou seja, quando a estratgia de bloqueio otimista DIRTY
utilizada apenas os campos atualizados so utilizados como verso. O sistema permite que atualizaes a atributos distintos no conflitem evitando OptimisticLockException
.
O diagrama acima pode ser resumido em:
- A primeira transao (Tx1) realiza a consulta de leitura da entidade Mesa com id igual a 1.
- A segunda transao (Tx2) simultaneamente tambm realiza a consulta de leitura da entidade Mesa com id igual a 1
- Tx1 altera a quantidade de lugares na mesa.
- Tx2 altera o status de disponibilidade da mesa
Construindo uma API REST para reserva de mesas com Versionless Optimistic Locking
Ao nvel de controller no existem mudanas a implementao do artigo anterior dado que nossas mudanas afetam apenas as entidades.
@PostMapping("/mesas/{id}/reservas")@Transactionalpublic ResponseEntity<?> reservar( @PathVariable(value = "id") Long mesaId, @RequestBody ReservaMesaRequest request, UriComponentsBuilder uriBuilder) { Mesa mesa = mesaRepository.findById(mesaId) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Mesa no cadastrada")); Usuario usuario = usuarioRepository.findById(request.getUsuarioId()) .orElseThrow(() -> new ResponseStatusException(UNPROCESSABLE_ENTITY, "Usuario no cadastrado")); LocalDateTime dataDaReserva = request.getDataReserva(); if (reservaRepository.existsMesaByIdAndReservadoParaIs(mesaId, dataDaReserva)) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Horario indisponivel para reserva"); } Reserva reserva = mesa.reservar(usuario, dataDaReserva); reservaRepository.save(reserva); URI location = uriBuilder.path("/mesas/{id}/reservas/{reservaId}") .buildAndExpand(mesa.getId(), reserva.getId()) .toUri(); return ResponseEntity.created(location).build();}
Independente da abordagem de bloqueio otimista sem verso indispensvel a construo de um Exception Handler para ObjectOptimisticLockingFailureException
, quando atualizaes conflitarem essencial que a resposta contenham um Http Status Conflict
.
@RestControllerAdvicepublic class DeafultExceptionHandler { @ExceptionHandler(ObjectOptimisticLockingFailureException.class) public ResponseEntity<?> optmisticLock(ObjectOptimisticLockingFailureException ex) { String msg = "Aconteceu um conflito em sua atualizao, tente novamente."; return status(409) .body( Map.of("mensagem",msg) ); }}
Concluso
Construir mecanismo para controle de concorrncia uma tarefa complexa dado que cada sistema possui suas restries. O mecanismo de Versionless Optimistic Locking do Hibernate oferece flexibilidade para implementao da estratgia de deteco de conflitos para diversos casos de uso.
Caso o conflito de atualizaes estejam causando integridade aos dados em um sistema legado ou quando alterar o schema seja custoso o OptimisticLockType.ALL
se torna uma tima soluo dado que seu comportamento semelhante ao Optimistic Locking tradicional.
Porm, se existe a necessidade de que atualizaes a atributos no sobrepostos no conflitem a estratgia de OptimisticLockType.DIRTY
se torna uma soluo j que apenas os atributos "sujos" so utilizados como verso da entidade.
Referncias
Original Link: https://dev.to/jordihofc/criando-sistemas-de-reservas-com-versionless-optmistic-locking-spring-boot-e-jpahibernate-7pe
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To