Como tratar exceções de duplicidade de chave utilizando JPA, EJB e CMT

Bom dia, pessoal!

Estou com uma dúvida conceitual sobre quais práticas são recomendadas no que diz respeito ao tratamento de exceções com JPA utilizando EJB e transações gerenciadas pelo container.

No caso em estudo, estou tentando “pegar” uma exceção de chave duplicada, algo bem bobo, como um email ou um nome de usuário que já existe na base de dados. Acontece que não consigo “pegar” a exceção de chave duplicada, pois como minhas transações são gerenciadas pelo container, ela só é lançada quando do commit (mas como é CMT, difícil saber quando isso vai acontecer, né?)

E outra, ainda que eu forçasse a situação (colocando um flush logo após o persist), o JPA me lança uma PersistenceException. Neste caso, seria melhor que lançasse EntityExistsException, pois do jeito que está teria que ficar percorredo o stackTrace para verifcar que exceção de fato aconteceu. Também não me parece ser de bom tom sair percorrendo o stackTrace e ficar analisando as exceções do hibernate/eclipselink ou do postgresql/mysql/etc, só para saber se houve uma exceção de duplicidade de chave, pois isso acaba por deixar meu código dependente do ORM e do DB.

Ainda assim, ficar colocando flush pelo caminho não me parece uma solução boa.

A solução que encontrei (que ao meu ver também não parece ser muito “bonita”, mas é a menos intrusiva) foi fazer uma busca na base antes de salvar o registro, e ao encontrar registro com mesma chave, lançar uma exceção minha, anotada com @ApplicationException(rollback=true). Mas teria alguma maneira mais elegante ou que utilize o JPA para capturar uma exceção de duplicidade de chave sem ter que ficar fazendo buscas na base de dados antes de efetuar o registro???

Tenho buscado bastante isso em fóruns e aqui mesmo no GUJ, mas ainda não encontrei uma maneira diferente de resolver isso.

Afinal, sobre as boas práticas no tratamento de exceções, a forma como estou fazendo está legal? Pois não vejo outra solução para casos como estes.

Realmente nunca vi algo “bonito” para isso.

Você poderia fazer:

  1. A pesquisa antes de inserir
  2. Flush
  3. Criar um método que abrisse uma nova transação (REQUIRES_NEW), e aí vocẽ faria um try/catch.

Na boa? Eu prefiro inserir, se deu exception (qualquer uma) eu jogaria como duplicado.

É difícil que um usuário chegue a uma tela de inserir se o banco estiver com problemas.

É arriscado, mas você evitará uma ida ao DB.

Valeu pela dica, Hebert! Utilizo muito seus modelos de aplicação para estudo!

De fato, pelo que vi é difícil dar outra exceção no JPA que não seja de chave duplicada, a não ser que o BD esteja down mesmo. Tem situações de exceção, mas são da transação, e essas eu nem vou me preocupar em tratar.

De qualquer forma, acho que vou continuar utilizando o tratamento dessa exceção como exceção de negócio, que no fundo é mesmo isso. E fazer uma busca por uma chave única no BD não chega a ser tão caro assim, pior fosse se buscasse por informação não indexada.

Valeu!

[quote=JeffersonFelix]Valeu pela dica, Hebert! Utilizo muito seus modelos de aplicação para estudo!

De fato, pelo que vi é difícil dar outra exceção no JPA que não seja de chave duplicada, a não ser que o BD esteja down mesmo. Tem situações de exceção, mas são da transação, e essas eu nem vou me preocupar em tratar.

De qualquer forma, acho que vou continuar utilizando o tratamento dessa exceção como exceção de negócio, que no fundo é mesmo isso. E fazer uma busca por uma chave única no BD não chega a ser tão caro assim, pior fosse se buscasse por informação não indexada.

Valeu![/quote]uhum. para sistema de alta demanda, ou de DB enorme isso faria diferença. [=

Bem, se desse modo funciona pra ti, go go go! \o/