Tratamento de concorrência na Java Persistence API - JPA

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. :slight_smile:

[Consegui resolver e até públiquei um post sobre o assunto (pouco explorado) - concorrência na JPA - http://archsofty.blogspot.com/2009/10/jpa-lock-otimista-e-pessimista.html ]

Amigo,

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.

Abraços!

Minha sugestão é de fazer um lock otimista.

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.

Isso te ajuda? Abraços

Complementando…

[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

}[/code]

Obrigado por todas as dicas, a partir dai ficou mais fácil pesquisar… até ousei a escrever algo sobre concorrência na JPA - http://archsofty.blogspot.com/2009/10/jpa-lock-otimista-e-pessimista.html