Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 17, 2022 10:00 pm GMT

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.

Diagrama de classe entidade Mesa
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.

Conflito em atualizaes com atributos no sobrepostos

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.

Atualizaes no sobrepostas

O diagrama acima pode ser resumido em:

  1. A primeira transao (Tx1) realiza a consulta de leitura da entidade Mesa com id igual a 1.
  2. A segunda transao (Tx2) simultaneamente tambm realiza a consulta de leitura da entidade Mesa com id igual a 1
  3. Tx1 altera a quantidade de lugares na mesa.
  4. 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

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To