Gostaria de saber como implementar : pessimistic | optimistic lock no JPA.
Penso que tudo se esclarecerá melhor com um exemplo:
Num cenario com três entidades - Produto {id, descricao, preco, quantidade} , ItemVenda {id, produto, preco, quantidade} e Venda{id, List itens, data, vendedor}.
Um processo que determina uma compra é: verificar se há a quantidade de produtos e diminuir essa quantidade.
[code]public void realizaCompra(){
tx.begin();
for (ItemVenda item : venda.getItensVendas()){
Produto produtoDoBanco = em.find(Produto.class,item.getProduto().getId());
if (produtoDoBanco.getQuantidade() < item.getProduto().getQuantidade()){
throw new NaoHaEstoqueException(“não há itens para essa venda”);
} else {
produtoDoBanco.diminuirUnidades(item.getProduto().getQuantidade());
em.persist(produtoDoBanco);
}
}
em.persist(venda);
tx.commit();
}[/code]
Melhorando minha pergunta… qual tipo de lock eu deveria usar? (ou um para cada…?)
No momento em que estou lendo a quantidade de produtos que há no estoque, ninguém deveria ser capaz de alterar esse valor … se não estiver claro … tentarei utilizar outras palavras.
Tente ver algo sobre a anotação “@Version”, to sem tempo agora pra te dar um exemplo de como usa-la, mas deve ter vários tutos pela internet, ou a própria documentação.
No seu método de negócio eu faria um refresh do objeto na base fazendo um lock no mesmo. Então nesse momento valido a quantidade lançando exception caso não houver mais produtos disponíveis; depois calculo a nova quantidade, salvo na base e libero o lock.
Usei essa mesma metodologia quando implementei o sistema de vendas de uma grande telecom aí. Funcionou muito bem.
[code]public void realizaCompra(){
tx.begin(); // você usa CMT ou BMP?
for (ItemVenda item : venda.getItensVendas()){
// carrega e efetua lock, libera após o transaction.commit ou transaction.rollback
Produto produtoDoBanco = em.find(Produto.class,item.getProduto().getId());
em.lock(produtoDoBanco, LockMode.WRITE);
if (produtoDoBanco.getQuantidade() < item.getProduto().getQuantidade()){
// deu erro, o JTA fará rollback
throw new NaoHaEstoqueException("não há itens para essa venda");
} else {
produtoDoBanco.diminuirUnidades(item.getProduto().getQuantidade());
}
}
em.merge(venda); // salva o produto
tx.commit(); // quando fizer commit o lock é liberado