1
resposta

Tratamento de exceção ConstraintViolationException vs PersistenceException

Boa tarde!

Implementei um método de teste usando TestNG com a intenção de capturar uma exceção que seria gerada ao tentar remover um registro de uma tabela que é referenciada por outra tabela. Nesse caso ocorreria uma tentativa de violação de constraint. Ao executar o código percebo no log de erro do TestNG que é gerada a exceção org.hibernate.exception.ConstraintViolationException, entretanto ao tentar capturá-la não consegui. Fazendo alguns testes percebi que a exceção que de fato é gerada é outra, javax.persistence.PersistenceException (ou alguma filha mais específica, que não descobri). Percebi que o valor “org.hibernate.exception.ConstraintViolationException” aparece como texto do método getMessage (da exceção capturada PersistenceException).

Fiz várias pesquisas e percebi que existe dificuldade com esse tipo de exceção há um bom tempo, mas não encontrei uma solução ou explicação legal.

Obs: Estou usando JPA com Hibernate.

Abaixo está o código que implementei "tentando" contornar esse problema:

@Test(dependsOnMethods = { "adicionaUnidadesAUmCursoExistente" },
        expectedExceptions = { ViolacaoDeConstraintException.class })
public void tentaRemoverUmCursoComUnidadesMasNaoConsegue() {

    Curso curso = cursoDAO.busca(cursoId);
    try {
        cursoDAO.remove(curso);
        em.flush(); // para que as constraints sejam validadas
    } catch (PersistenceException e) {
        /*
         * não é possível capturar ConstraintViolationException. Ela consta
         * como resultado (texto) do e.getMessage().
         */
        if (e.getMessage().contains("ConstraintViolationException")) {
            throw new ViolacaoDeConstraintException(e.getMessage());
        } else {
            throw e;
        }
    }
}

O Try/Catch não seria necessário, caso fosse possível capturar a exceção correta.

A tentativa inicial foi por capturar ConstraintViolationException, mas isso me cheirou mal pelo fato de que essa é uma exceção do Hibernate e não da JPA. E a ideia seria não vincular o código a recursos específicos de um privider ORM. Mas como essa foi a exceção apresentada, comecei por ela. Depois percebi que a exceção lançada é da JPA, e não é mostrada no log. Descobri fazendo testes.

Imagino que o melhor mesmo seria trabalhar com exceções da JPA, mas analisando o javadoc não descobri uma exceção mais específica que PersistenceException (equivalente à ConstraintViolationException do Hibernate). Eexiste?

Imagino que o melhor seria um código semelhante ao que segue:

@Test(dependsOnMethods = { "adicionaUnidadesAUmCursoExistente" },
        expectedExceptions = { NomeDaExcecaoEspecificaDaViolacaoDeConstraintDoJPA.class })
public void tentaRemoverUmCursoComUnidadesMasNaoConsegue() {

    Curso curso = cursoDAO.busca(cursoId);
    cursoDAO.remove(curso);
    em.flush(); // para que as constraints sejam validadas
}

Estou aprendendo a trabalhar com testes nesse projeto acadêmico. Talvez esse tipo de teste não seja um "teste de valor" (testar constraints?), mas como sou iniciante acho que ta valendo. Preciso praticar e aprender como as coisas funcionam.

Coisarada...

Alguém poderia me ajudar a esclarecer essas questões?

Desde já agradeço.

1 resposta

Oi Rodrigo, tudo bom?

Entendo a sua questão e realmente é complicado capturarmos este tipo de exceção pois a JPA "embrulha" as exceções dos ORMs justamente para seu código não ficar acoplado à uma implementação específica.

Outro ponto é que seu código não deveria se basear nas constraints no banco para saber se deu certo ou não, o ideal é que você possua validações lógicas que verifiquem o que pode ser feito na aplicação, independente do banco de dados.

Regras de negócio são melhores em código, o banco deveria ser apenas um fallback para garantir que realmente não tem nenhuma inconsistência.

Abraço!