Tratamento de Execeções em camadas

64 respostas
D

Estou capturando a execeção na camada de modelo e gostaria de apresentar ela na camada de view

DAO Generico - Model

@Override
    public void adicionar(Object obj){
        
       try{
           
           sessao = HibernateUtil.getSessionFactory().openSession();
           transacao = sessao.beginTransaction();
           sessao.save(obj);
           sessao.flush();
           transacao.commit();
           
       }catch(ConstraintViolationException){
             //mensagem desejada
       }
}

PessoaFacesBean - Controller

public void salvar(){
       try{
            
           pessoaManager.adicionar(pessoa);

           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Aviso", "Pessoa Salva com Sucesso!"));
            
        }catch(Exception e){
            
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Aviso", MENSAGEM DE ERRO VINDA DO MODEL));
        
        }
}

Qual seria a melhor forma para trazer essa mensagem ?

64 Respostas

D

se eu não me engano é no “Managed Bean” que vc deve tratar as exception, similar ao Action do Struts. Ou seja, onde estorar a Exception você da um throw até chegar no controller para você tratar la.

D

Mais devo tratar exeções de banco de dados no “Managed Bean” ?

D

no método que vc estiver desenvolvendo no DAO ou qualquer outro com acesso ao banco vc da um “throws Throwable” e no “Managed Bean” você trata com try/catch entendeu?

D

sim entendi , e para mostrar a mensagem do ConstraintViolationException no “managed bean” ? porque no model vou ter mais de uma exeption

D

Como é uma exception exclusiva vc cria um catch somente para essa exception. O resto das exception principalmente de Banco vc joga no Throwable.

D

eu tentei fazer isso , deixei uma catch capturando exepetion no model e fiz uma ConstraintViolationException no “managed bean” mais ele já foi capturado pelo model … ( isso que você quis dizer ? )

D

não entendi sua pergunta, apartir do momento que você da um “throw new Exception()” ele joga para a camada de controler. Agora se vc esta tratando antes no model, nao ira chegar lá vc concorda?

qualquer coisa posta ai…

D

concordo , isso que eu falei que testei … ok vou fazer uns tentes com throw

F

Um sistema não pode emitir mensagens de erros com detalhes tecnológicos. Qualquer problema no sistema, o usuario final deve receber mensagens compatíveis com a usuabilidade do sistema.
Como fazer?

  • Toda as camadas logica da solução deve encapsular os detalhes tecnologicos emitindo, tratando todas as exceptions devidadmente e propagando mensagens em níveis de negocio ou de serviço.
D

Isso que eu quero fazer Fernando , capturar uma ConstraintViolationException na camada da model e apresentar uma mensagem de “Esse registro já existe” para o usuário

F

Normal…
Defina pelo menos uma exception para cada camada.
Persistência - AcessoException
Negocio - NegocioExcetoptin
Visão - VisaoException

Na camada de visão vc pega o texto e imprime para o usuario.

D

minha dúvida é exatamente essa ! pegar o texto de uma camada e passar para um FacesContext , poderia me passar um exemplo ?

Obrigado

F

ddark.emanu:
minha dúvida é exatamente essa ! pegar o texto de uma camada e passar para um FacesContext , poderia me passar um exemplo ?

Obrigado

D

Fiz dessa maneira :

@Override
    public void adicionar(Object obj){
        
       try{
           
           sessao = HibernateUtil.getSessionFactory().openSession();
           transacao = sessao.beginTransaction();
           sessao.save(obj);
           sessao.flush();
           transacao.commit();
           
       }catch(ConstraintViolationException c) {
           throw new ConstraintViolationException("Esse Registro já existe", null, null);
       }catch(Exception e){
           
           transacao.rollback();
           
       }finally{
           
           sessao.close();
           
       }
        
    }

ele enviou o erro para a próxima camada

public void salvar(){
        
        try{
            
           pessoaManager.adicionar(pessoa);
           
           pessoa = new Pessoa();
           pessoas = new ArrayList<Pessoa>();
           
           listar();
        
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Aviso", "Pessoa Salva com Sucesso!"));
            
        }catch (ConstraintViolationException cx){
                FacesContext faces = FacesContext.getCurrentInstance();
                faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL,"Aviso", cx.getMessage()));
        }catch(Exception e){
            
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Aviso", "Instabilidades ao Salvar Pessoa."));
        
        }
   }

mais aqui ele não capturou no ConstraintViolationException foi direto para o Exception , é dessa maneira mesmo que devo fazer ? o que há de errado.

Erro

AVISO: SQL Error: 1062, SQLState: 23000
GRAVE: Duplicate entry 'aaa' for key 'PRIMARY'
GRAVE: Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: could not insert: [model.pessoa.Pessoa]
	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
...
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'aaa' for key 'PRIMARY'
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
....
AVISO: A system exception occurred during an invocation on EJB PessoaManagerImpl method public void util.comandos.comandosManagerImpl.adicionar(java.lang.Object)
javax.ejb.EJBException
	at com.sun.ejb.containers.BaseContainer.processSystemException(BaseContainer.java:5193)
....
Caused by: org.hibernate.exception.ConstraintViolationException: Esse Registro  existe
	at util.comandos.comandosManagerImpl.adicionar(comandosManagerImpl.java:28)

estou capturando a execption errada ?

F

Sim…vc não pode lançar a mesma exception que tratou dentro do salvar.
Na verdade, a forma flexível é vc criar uma exception padrão sua de serviço que reflita erros da própria camada e lançar, emitindo uma mensagem padrão de serviço.
A camada adjacente usuário deve deter conhecimentos das exceptions internas ocorridas.

D
catch(ConstraintViolationException ce){
           
           throw new RegistroDuplicado("Esse Registro já Existe", ce.getSQLException(), ce.getConstraintName());
           
       }
}catch(RegistroDuplicado rd){
            
            FacesContext faces = FacesContext.getCurrentInstance();
            faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL,"Aviso", rd.getMessage()));
                
        }

Continua o problema de não captura - la na camada de cima .

F

Fico ok agora!!!

Continua oq??

D
public void salvar(){
        
        try{
            
           pessoaManager.adicionar(pessoa);
           
           pessoa = new Pessoa();
           pessoas = new ArrayList<Pessoa>();
           
           listar();
        
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Aviso", "Pessoa Salva com Sucesso!"));
            
        }catch(RegistroDuplicado rd){ // ele não entra nesse catch
            
            FacesContext faces = FacesContext.getCurrentInstance();
            faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL,"Aviso", rd.getMessage()));
                 
        }catch(Exception e){ // ele sempre apresenta esse catch , caso eu tirar ele , não apresenta nada 
            
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Aviso", "Instabilidades ao Salvar Pessoa."));
        
        }
   }
F

Vc ta lançando a exception devidamente no método salvar()…o código q vc postou não esta!

D

Estou lançando aqui

@Override
    public void adicionar(Object obj){
        
       try{
           
           sessao = HibernateUtil.getSessionFactory().openSession();
           transacao = sessao.beginTransaction();
           sessao.save(obj);
           sessao.flush();
           transacao.commit();
           
       }catch(ConstraintViolationException ce){
           
           throw new RegistroDuplicado("Esse Registro já Existe", ce.getSQLException(), ce.getConstraintName());
           
       }catch(Exception e){
           
           transacao.rollback();
           
       }finally{
           
           sessao.close();
           
       }
        
    }

e capturando no salvar

F

Sua exception deve ser checkada!! como vc fez?

T

Olá. Vou mostrar como geralmente realizo o tratamento de exceções.

Tomemos um exemplo. Vamos supor que tenho a seguinte classe Usuario:
class Usuario {
    private String login, senha;
    
    // Getters e setters.
}
Agora tenho um service que faz o cadastro de um novo usuário usando um DAO:
class UsuarioService {
    public void create(String login, String senha) {
        Usuario usuario = new Usuario(login, senha);
        usuarioDao.create(usuario);
    }
}
E tenho um managed bean que contém um método que usa o service:
class UsuarioBean {
    public void criarUsuario() {
        usuarioService.create(login, senha);
    }
}
Partindo do cenário acima, vamos supor que quero inserir a seguinte regra:
Os campos login e senha não podem ser vazios.
Vamos começar criando uma classe para representar erros de negócio, conforme sugerido pelo colega FernandoFranzini:
class BusinessException extends RuntimeException {
    public BusinessException(String message) {
        super(message);
    }
}
Reparem que a exceção herda de RuntimeException. Isso é para evitar que precisemos declarar throws Exception em cada método que trata essa exceção. Agora vamos modificar a classe Usuario para validar nossa regra:
class Usuario {
    private String login, senha;
    
    public Usuario(String login, String senha) {
        if (String.isEmpty(login) || String.isEmpty(senha)) {
            throw new BusinessException("Campos requeridos não preenchidos.");
        }
    }
    
    // Getters e setters.
}
Nosso service deve ser modificado agora para realizar esse tratamento, e propagar essa exceção para o managed bean. Se quisermos, podemos usar a idéia do colega FernandoFranzini e criar também uma exceção para representar erros nos services. Afinal, aqui podem acontecer erros de vários tipos além de erros de negócio.
class ApplicationException extends RuntimeException {
    public ApplicationException(Throwable cause) {
        super(cause);
    }
}
Observem que na exceção anterior estamos passando pro construtor da superclasse a causa do erro. Isso é muito importante! Devemos sempre criar nossas exceções passando também a exceção que representa a causa. Segue abaixo o service modificado:
class UsuarioService {
    public void create(String login, String senha) {
        try {
            Usuario usuario = new Usuario(login, senha);
            usuarioDao.create(usuario);
        catch (BusinessException ex) {
            throw new ApplicationException(ex);
        }
    }
}
Então, quando a exceção chegar no managed bean, fazemos o tratamento necessário:
class UsuarioBean {
    public void criarUsuario() {
        try {
            usuarioService.create(login, senha);
        } catch (ApplicationException ex) {
            // Exibir mensagem de negócio para o usuário.
        }
    }
}
V

Acho essa abordagem um tanto ultrapassada. Essa perspectiva leva à criação de inúmeras exceptions desnecessárias, uma para cada camada, que nada faz além de conter a lançada pela camada anterior - o que chega na camada de visão é uma exceção de visão que contém uma de negócio, que contém uma de persistência, um pacotão.

Já discuti com várias pessoas sobre a melhor forma de administrar as exceções, e até um tempo atrás, usávamos um interceptor que capturava todas as exceções - nós lançavamos e só eram pegas aquelas referentes ao comportamento do próprio sistema (LoginInvalidoException, por exemplo), as demais exceções eram tratadas por esse interceptor. Já é uma abordagem melhor, mas aí percebi que era um trabalho a mais, pois á um mecanismo nativo que faz isso: a tag error-page do web.xml (falando aqui de sistemas web, claro). Nela pode-se especificar qual exception leva para qual página de erro, assim você pode lançar as exceções de banco, de envio de email, etc, e não se preocupar em qual camada ela deve ser tratada, isso será feito na mais alta.

Flw! :thumbup:

D

não fiz checagem … apenas capiturei a exception(ConstrantViolation) no método adicionar e criei uma nova exception (RegistroDuplicado) e no metodo salvar (na camada de Controller) executei o metodo adicionar onde ocorre o erro de ConstrantViolation e tentei capturar a execption que criei(RegistroDuplicado) para enviar uma mensagem personalizada para tela.

está errado esse pensamento ?

D

tnaires:
Olá. Vou mostrar como geralmente realizo o tratamento de exceções.

[/code]
Então, quando a exceção chegar no managed bean, fazemos o tratamento necessário:

class UsuarioBean { public void criarUsuario() { try { usuarioService.create(login, senha); } catch (ApplicationException ex) { // Exibir mensagem de negócio para o usuário. } } }

Acredito que fizemos diferente e chamos ao mesmo lugar no final …

mas no final não consegui capturar a execption igual vc capturou no criarUsuario , acredito que seja pela sua validação

if(objeto.isEmpty)
     throw new Exeption()

mas como vou fazer essa validação se é erro de banco (ConstrantViolation) ?

obs : me corrija se eu estiver errado

F

Pode existir aplicações que não contenham regras de negocio complexa que com uma exception genérica por camada já resolve o problema…vendo que hoje um sistema distribuído multicamadas pode chegar em 4 ou 5 camadas logicas e/ou distribuídas…sua justificativa de números de exceptions é inconsistente.

E outra, pode existir aplicações que contenham regras de negocio complexa e por isso exista a necessidade varias exception por erro de negocio…tudo esta relacionado com necessidade…justificar com algo ultrapassado tb é inconsistente…Pode não ser o seu cenário mas pode ser de milhões outros.

V

FernandoFranzini:
Pode existir aplicações que não contenham regras de negocio complexa que com uma exception genérica por camada já resolve o problema…vendo que hoje um sistema distribuído multicamadas pode chegar em 4 ou 5 camadas logicas e/ou distribuídas…sua justificativa de números de exceptions é inconsistente.

E outra, pode existir aplicações que contenham regras de negocio complexa e por isso exista a necessidade varias exception por erro de negocio…tudo esta relacionado com necessidade…justificar com algo ultrapassado tb é inconsistente…Pode não ser o seu cenário mas pode ser de milhões outros.


O caso é que exceções genéricas não fazem sentido. Lançar DaoException ou BusinessException não diz nada, o ideal seria lançar uma exceção para uma dada situação, como FundosInsufucientesException. E não importa o nível de complexidade das regras de negócio do sistema, o que importa é que as exceções sejam representativas dentro do domínio, seja simples ou não.

Acho que não fui claro no seguinte, não sou contra criar várias exceções para n situações, o que disse é que você não precisa lançar exceções de banco que devem ser pegas na camada de negócio, que vai empacotá-la com outra e mandá-la para a camada view, isso que é uma abordagem ultrapassada. Se você recebe uma SQLException ao não conseguir conectar no banco, é realmente necessário empacotá-la? Isso é que deve ser levado em consideração. Ao dizer que deve haver uma exceção genérica para cada camada, fica a clara impressão de que você precisa passar por elas quando um erro ocorre, o que não é verdade. Se a exceção deve ser tratada de uma camada para a outra (não encontrou o login), ok, sem problemas, mas uma exceção de que o banco está fora não precisa ser capturada na camada de negócio, se for apenas informar para o usuário que ele deve tentar mais tarde. Em resumo, cuidado com essas obrigatoriedades entre camadas! :smiley:

Flw!

F

Logico que faz…desde que a mensagem de erro esteje clara dentro do objeto, uma vez que duas exception pode ser do mesmo tipo, com diferentes significados. Existem inúmeros casos dentro de varias especificações: JDBC, JPA etc…

Eu tb concordo…embora muitas produtos façam isso… minha dica não foi para empacotar…foi para TRADUZIR e repassar outra exception em nível de serviço.

Eu entendi sim…acho muito legal sua opinião e todos crescemos com isso…eu particularmente só acho que quando vc invalidar uma opções arquitetural vc tem q ter justificativas coerentes… cenário versus pros e contras…

V

Não, não faz. Não existe uma JPAException na JPA nem uma JDBCException no JDBC. Preste atenção amigo, há uma grande diferença entre SQLException e DaoException.

Ainda sim, não há o que traduzir numa exceção que indica que o banco está fora. Não é porque é uma exceção gerada no banco que deve necessariamente ser traduzida para ser passada para a frente, já que o que ela representa é mais do que claro.

F

kkkkkkk impasse total !!! :smiley:

Unica diferença é semântica…ou seja o que eles significam, no resto foi mais um objeto e caso exception de erro…

Ainda sim, não há o que traduzir numa exceção que indica que o banco está fora. Não é porque é uma exceção gerada no banco que deve necessariamente ser traduzida para ser passada para a frente, já que o que ela representa é mais do que claro.

Muito bem…

  • Vc fez um DAO que acessa seu mecanismo de persistência…banco de dados relacional. Vc é obrigado pelo JDBC resolver as SQLException…oq vc faz? joga para cima? ok…a camada usuaria desse DAO é obrigado a tratar SQLException.

  • Suponhamos que vc muda de filosofia de banco de dados OO…quando vc mudar o DAO, vai usar outra coisa diferente de JDBC para acessar esse bancoOO…usar API para uma bancoOO…que por sua vez lança outra exception de acesso…dai erroneamente vai jogar estas outras para fora tb…quebrando o código da outra camada tratadora…
    Ou seja…quando o DAO mudar internamente vc acaba tendo que alterar a objeto da camada que ta usando esse dao…manutenção ZERO!

  • O mais flexível é o DAO lançar uma exception padrão do sistema de forma que encasule e não propague detalhes da API’s internas usados nas diferentes. Com isso não importa se o DaoImp1 usa JDBC ou DaoImp2 usa bancoOOP…a camada cliente da camada DAO nunca vai saber…

A não ser q vc tenha um abordagem diferente com os mesmos benefícios e flexibilidade…
Se tiver eu gostaria de aprender…

V

Não é semântica. SQLException é representativa, DaoException é genérica. Se não consegue ver isso, creio que não adianta eu tentar ficar explicando.

FernandoFranzini:
Muito bem…

  • Vc fez um DAO que acessa seu mecanismo de persistência…banco de dados relacional. Vc é obrigado pelo JDBC resolver as SQLException…oq vc faz? joga para cima? ok…a camada usuaria desse DAO é obrigado a tratar SQLException.

  • Suponhamos que vc muda de filosofia de banco de dados OO…quando vc mudar o DAO, vai usar outra coisa diferente de JDBC para acessar esse bancoOO…usar API para uma bancoOO…que por sua vez lança outra exception de acesso…dai erroneamente vai jogar estas outras para fora tb…quebrando o código da outra camada tratadora…
    Ou seja…quando o DAO mudar internamente vc acaba tendo que alterar a objeto da camada que ta usando esse dao…manutenção ZERO!

  • O mais flexível é o DAO lançar uma exception padrão do sistema de forma que encasule e não propague detalhes da API’s internas usados nas diferentes. Com isso não importa se o DaoImp1 usa JDBC ou DaoImp2 usa bancoOOP…a camada cliente da camada DAO nunca vai saber…

A não ser q vc tenha um abordagem diferente com os mesmos benefícios e flexibilidade…
Se tiver eu gostaria de aprender…


Nesse caso, SQLException foi um exemplo infeliz, por ser uma exceção checada (estou tão acostumado a trabalhar com Spring e lidar apenas com DataAcessException que havia me esquecido disso). Então, vamos dizer que foi criada uma DataAcessException, exceção de runtime, apenas para empacotar a SQLException. Por ser uma exceção não checada, não preciso pegá-la. Usando o modelo proposto antes, só preciso de uma configuração no web.xml, e assim só a pego onde há sentido para o negócio, do contrário passa direto e o usuário recebe a mensagem adequada. Repare que trabalhando com exceções não checadas, não precisamos dessa tradução proposta, já que ela não fica poluindo o sistema. Agora, se for preciso mudar de banco ou de api, posso até precisar mudar minha DataAcessException, mas é só: ainda vai ser pega onde deve ser pega, e vai continuar apresentando a mensagem ao usuário quando tiver de ser assim.

Acredite, tenho que resolver sérios problemas com exceções que “devem” ser lançadas entre as camadas, que no fim só poluem o sistema.

F

Pois é, dessa forma só funciona se for a camada cliente invocado for uma camada servlet no qual existe o recurso do error-code no web.xml. Caso seje outra camada…não daria certo…
O que vai acontecer nessa nova camada de sistema não tratar uma runtime excepton quando ela acontecer? O programa simplesmente para…

Conceitualmente falando…
Exceptions chekadas são erros que o programa pode se recuperar…
Exceptions não-chekadas são erros que o programa não pode se recuperar…por isso não faz sentido tratar com try…o que vc pode fazer se deu pau no disco?
Baseado nisso, colocar todas as exceptions como de uma solução não-chekadas pq vc “acha” que ta “poluindo”…fura todo conceito…

Opinião final

  1. Criar uma camada DAO que lança exceptions não-chekadas e tratar com error-code do web.xml é uma abordagem aceitável mas pouco flexível e propenso e erros futuros. Só daria certo para um sistema em 2 camadas VIEW->DAO e view precisaria se servlet. Caso o sistema evolua para outras camadas, os programadores da equipe poderiam não tratariam as exceptions com try/cacth propagando erros em tempo de runtime.

Eu ainda voto para criar 1 exception genérica ou varias tipadas chekadas mesmo…

Spring é mais um caso que usa a abordagem que cria exception em nível de serviço, encasulando o tratamento interno
Ja o caso do spring é a mesma coisa…Na pagina 127 do Spring In Action existe a documentação das exceptions que o spring criou para TRADUZIR as exceptions de JdbcSupport com o objetivo de fornecer independência de provedor de JDBC… Veja que o próprio spring implementou varias exceptions para desacoplar a classe JdbcSupport dos erros JDBC. O caso delas serem não-checakas da certo uma vez que os objetos estarem RODANDO dentro do Spring que OBVIAMENTE faz o tratamento adequado de try para essas exception não chekadas. Isso só reafirma as minhas sugestões iniciais.

V

Estamos falando de um sistema web, se for para outra plataforma, obviamente a abordagem seria outra.

Sinto lhe informar que essa visão de exceptions também é ultrapassada. Você já viu algum framework atual lançar exceções checadas, salvo raríssimas exceções? Já leu o livro Clean Code? Vou citar uma parte:
“At the time, we thought that checked exceptions were a great idea; and yes, they can
yield some benefit. However, it is clear now that they aren?t necessary for the production of
robust software. C# doesn?t have checked exceptions, and despite valiant attempts, C++
doesn?t either. Neither do Python or Ruby. Yet it is possible to write robust software in all
of these languages. Because that is the case, we have to decide?really?whether checked
exceptions are worth their price…
…Checked exceptions can sometimes be useful if you are writing a critical library: You
must catch them. But in general application development the dependency costs outweigh
the benefits.”

Sobre a sua opinião final, se considera que os programadores da sua equipe não testariam (só assim para não tratar possíveis exceções) para saber onde tratar, então você parte do principio que as pessoas não fazem o trabalho direito, e isso não é um ponto de vista razoável.

Claro que o Spring criou suas exceções para empacotar as do jdbc, ninguém quer trabalhar com exceções checadas. E não tem nada a ver com elas serem utilizadas dentro do Spring, porque ele as lança quando ocorre um erro. Já se perguntou porque ele não lança nenhuma exceção checada? Ou porque python ou C# não tem? Pense no assunto.

F

Eu conheço o livro sim, mas não assimilei ainda como uma aplicação pode se denominar “robusta” lançando exceptions não chekadas e ainda permanecer em pé…Mas enfim…volto a te perguntar…
Uma camada lançando exception não checkadas…podendo ser invocado por N tipos de camadas…como fazer isso ficar robusto? O que vc faz?
Como essas novas linguagem dinâmicas que não tem checked exceptions trata isso para não parar o programa?

V

Pense da seguinte forma, checadas ou não, estamos falando de exceções. Concorda que, basicamente, a diferença entre as duas é que com a checada você é obrigado a pegá-la, e com a não checada, não precisa - mas não quer dizer que não vai! Eu gosto de dizer que eu tenho a confiança de que aquele que tem de pegá-la, vai fazê-lo no momento mais adequado. Você pode fazer um sistema apenas com exceções checadas, mas não é isso que o torna um sistema robusto - nesse caso ele ficaria totalmente poluído, concorda?

No python, por exemplo, exceções checadas nem existem, e ainda sim sistemas muito robustos são contruídos, capturando as não checadas quando é necessário. A idéia aqui é: não é porque você não é obrigado à fazê-lo, que não será feito.

Isso é justamente o que me fez gostar tanto do Python, ele confia em mim! :mrgreen:

F

Pense da seguinte forma, checadas ou não, estamos falando de exceções. Concorda que, basicamente, a diferença entre as duas é que com a checada você é obrigado a pegá-la, e com a não checada, não precisa - mas não quer dizer que não vai!
Sim…

Então, tenho feito sistemas assim com 2, 3 , 4 camadas JEE ha + de 7 anos que posso te dizer que eu discordo…talvez por costume mesmo…Mas estou aberto para novos paradigmas como esse ai… :smiley:

Entendi…
Então eu poderia…fazer um sistema completo, em camadas lançando exceptions não-checkadas…e tratar somente os pontos críticos?
Caso acontece um erro em um ponto não critico…o sistema vai parar certo? Ou tem algum recurso novo ai?

V

FernandoFranzini:
Entendi…
Então eu poderia…fazer um sistema completo, em camadas lançando exceptions não-checkadas…e tratar somente os pontos críticos?
Caso acontece um erro em um ponto não critico…o sistema vai parar certo? Ou tem algum recurso novo ai?

Sim, eu nunca lanço exceções checadas, e isso tornou o código dos sistemas aqui muito mais limpos. É você quem decide se e onde as exceções lançadas devem ser capturadas, não importa se é um ponto crítico ou não. Esses são critérios que você é que define.
Por exemplo, ao buscar um usuário no banco, você recebe uma ResultadoVazioException (exceção de banco). Se ela só acontece aqui, fica fácil permitir que ela vá até o ponto mais alto para que o web-xml mapeado leve o usuário para a página de login inválido. Consegue ver o ganho? O sistema não vai parar porque quando você não pega a exceção, é porque você tem planos pra ela, então não pegar a exceção também faz parte da estratégia! Usar a exceção não checada não quer dizer que você não vai tratá-la quando deve, mas ao menos você não é obrigado a fazê-lo, como no caso da checada! Isso permite mais maior flexibilidade na forma de administrar as exceções.

Não sei se consegui ser claro o suficiente. Se você ainda tem dúvidas, consegue pensar em alguma situação que possa ilustrar a sua preocupação com os pontos críticos?

F

Fico claro sim, nessa abordagem vc acaba tendo que colocar varias tratamentos de <exception-type> no web.xml, deixando realmente o código das camadas mais limpo sem os try. Tranquilo!!! Agradeço a paciência. Pode ter certeza que essa visão sua ja esta no meu MAPA de decisões arquiteturais.
Valeu :smiley:

D

Como eu faria a checagem ? o catch não teria que capturar o RegistroDuplicado lançado pelo metodo adicionar ?

Controller

public void salvar(){
        
        try{
            
           pessoaManager.adicionar(pessoa);
           
           pessoa = new Pessoa();
           pessoas = new ArrayList<Pessoa>();
           
           listar();
        
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Aviso", "Pessoa Salva com Sucesso!"));
            
        }catch(RegistroDuplicado rd){
            
            FacesContext faces = FacesContext.getCurrentInstance();
            faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL,"Aviso", rd.getMessage()));
                
        }catch(Exception e){
            
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Aviso", "Instabilidades ao Salvar Pessoa."));
        
        }
   }

Model

@Override
    public void adicionar(Object obj){
        
       try{
           
           sessao = HibernateUtil.getSessionFactory().openSession();
           transacao = sessao.beginTransaction();
           sessao.save(obj);
           sessao.flush();
           transacao.commit();
           
       }catch(ConstraintViolationException ce){
           
           throw new RegistroDuplicado("Esse Registro já Existe", ce.getSQLException(), ce.getConstraintName());
           
       }catch(Exception e){
           
           transacao.rollback();
           
       }finally{
           
           sessao.close();
           
       }
        
    }
V

Se a ConstraintViolationException só ocorre nesse caso, você não precisa pegá-la, mas imagino que não é o caso, então poderia deixar a RegistroDuplicado passar até o topo. Aliás, pra dar catch em Exception no método salvar?

Repare que o que sugeri ao longo desse tópico refere-se ao web.xml, e estava me referindo à uma aplicação action-based. Talvez não seja a melhor abordagem para o JSF, então dê uma estudada em como esse mecanismo funcionaria com esse framework.

F

von.juliano:
Se a ConstraintViolationException só ocorre nesse caso, você não precisa pegá-la, mas imagino que não é o caso, então poderia deixar a RegistroDuplicado passar até o topo. Aliás, pra dar catch em Exception no método salvar?

Repare que o que sugeri ao longo desse tópico refere-se ao web.xml, e estava me referindo à uma aplicação action-based. Talvez não seja a melhor abordagem para o JSF, então dê uma estudada em como esse mecanismo funcionaria com esse framework.


Funciona simmm…é só declarar no web.xml da mesma forma…

D

O RegistroDuplicado ocorre quando duplico um registro único, gostaria de fazer essa validação a nivel de usuário , por exemplo se o usuário tentar registrar uma pessoa já existente retornar uma mesangem que esse usuário já existe
O catch exception na realizada não é nescessário , mais coloquei ali para enviar alguma mensagem de erro ao usuário.

von.juliano:

Repare que o que sugeri ao longo desse tópico refere-se ao web.xml, e estava me referindo à uma aplicação action-based. Talvez não seja a melhor abordagem para o JSF, então dê uma estudada em como esse mecanismo funcionaria com esse framework.

reparei , mais desse modo caso ocorra a exception ele redirecionaria para um outra página correto ? não conseguiria retornar um faces message na tela correto ?

V

Sim, mas aí ele faria um redirecionamento. Com jsf, fica legal aprensentar a mensagem durante a exibição de um componente, e tudo mais. A minha dúvida é se é possível fazer isso, se não dificulta muito. Aqui temos uma situação parecida, estamos fazendo um sistema que usa o jquery-ui, e nesse caso a abordagem muda um pouco, mas a idéia é a mesma.

Isso eu não sei te dizer. :? Em todo caso, talvez seja necessário criar um filter para pegar a exceção e criar a faces message, mas é uma sugestão de quem não tem tanta prática com essa ferramenta, então é o que eu faria em principio - mas talvez haja uma forma melhor.

D

mais pensando em java , sem frameworks , o catch RegistroDuplicado teria que ser capturado no salvar não teria ?

V

Depende. É preciso um contexto para poder responder isso. Se for em um sistema web, não.

D

isso que eu queria entender … porque não ?

V

Porque você pode mapear no seu web.xml e ele vai direcionar para a página de erro sozinho, assim você não precisa ficar pegando cada exceção que seu sistema lança, somente quando for necessário.

D

entendi esse mapemento no web.xml , mais você disse que em um sistema web , na camada de controller ele não captura a exceção(RegistroDuplicado) no catch do método salvar , não entendi o conceito ?porque não captura …

V

Porque estando no web.xml, isso é feito automaticamente. Se for para capturar, você vai fazê-lo apenas para colocar a mensagem no contexto, certo? Ao invés disso, você pode criar uma página que apresenta a mensagem que a exceção carrega consigo.

D

Entendi o que você falou e com certeza funciona perfeitamente!!

Mais vai ficar um pouco fora do patrão que estou trabalhando no sistema e gostaria de de entender porque o catch não captura …

V

Porque é trabalho desnecessário, dessa forma você vai ter que fazer o catch em todos os controllers, capturando todas as exceções que os daos lançam, quando poderia apenas passar isso para o web.xml.

D

Sim com certeza é mais trabalhoso,sem duvida o código fica mais limpo, mas na realidade é um projeto pequeno para a pós graduação, e estou testando esse conceito de exceções entre camadas tratando elas com mensagens do faces (JSF) em pop-ups do primefaces .

A questão na verdade não é nem trabalho("retrabalho "), a questão é que não funciona

V

Amigo, me pergunto se você leu toda a discussão do tópico. Fala sobre o “conceito de exceções entre camadas”, e nada mais.

Só porque é um projeto pequeno de graduação, dar mais trabalho e o código não ficar mais limpo não faz diferença?

Bom, a explicação está toda aí. Funciona, é uma abordagem muito interessante.

D

Li todo tópico, sem dúvidas é uma abordagem muito interessante e vou utiliza - la daqui para frente em novos projetos.

von.juliano:

Só porque é um projeto pequeno de graduação, dar mais trabalho e o código não ficar mais limpo não faz diferença?

Sem dúvida faz muito diferença, mais como disse só gostaria de entender o porque de “não funcionar.”

von.juliano:

Bom, a explicação está toda aí. Funciona, é uma abordagem muito interessante.

Sem dúvidas, muito intressante

V

Acho que não entendi o que você quer dizer por “não funciona”. :?

D

No começo do tópico eu perguntei uma maneira para trabalho com exceções em camadas(MVC), me disseram que uma boa prática seria lançar excpetion personalizadas nas camadas de baixo(model) para apresenta - las na camada de cima (Controller) , foi onde adotei essa maneira, mais está acontecendo um erro e eu não entendi o porque acontece por isso disse “não funciona” .

V

Certo, então considere que você não precisa de uma exception especifica para cada camada, e que elas devem ser representativas quanto ao que as geraram, não se baseie em de qual camada ela veio (DaoException por exemplo).

D

Certo …Olha minha situação então :

public void salvar(){
        
        try{
            
           pessoaManager.adicionar(pessoa);
           
           pessoa = new Pessoa();
           pessoas = new ArrayList<Pessoa>();
           
           listar();
        
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Aviso", "Pessoa Salva com Sucesso!"));
            
        }catch(ConstraintViolationException rd){ // como vou capturar essa excption aqui ? ( Aqui ele não capturar a ConstraintViolationException, por isso disse que não funciona )
            
            FacesContext faces = FacesContext.getCurrentInstance();
            faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL,"Aviso", rd.getMessage()));
                
        }catch(Exception e){
            
           FacesContext faces = FacesContext.getCurrentInstance();
           faces.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Aviso", "Instabilidades ao Salvar Pessoa."));
        
        }
   }
@Override
    public void adicionar(Object obj){
        
       try{
           
           sessao = HibernateUtil.getSessionFactory().openSession();
           transacao = sessao.beginTransaction();
           sessao.save(obj);
           sessao.flush();
           transacao.commit();
           
       }catch(ConstraintViolationException ce){
           transacao.rollback();
           throw new ConstraintViolationException ("Esse Registro já Existe", ce.getSQLException(), ce.getConstraintName()); //estou fazendo mal uso do throw ? ( Aqui ele capturar aConstraintViolationException )
           
       }catch(Exception e){
           
           transacao.rollback();
           
       }finally{
           
           sessao.close();
           
       }
        
    }
F

vc ja depurou para ver se ta lançando? acho q naõ esta entrando no throw new …

D

já … ele lança uma ConstrantVioletExpetion , depois uma BatchUpdateExpetion e por fim lança a ConstrantVioletExpetion com minha mensagem personalizada

No método salvar o catch(ConstrantVioletExpetion) não captura só no catch(Excpetion)

F

Estranho…então vc não ta jogando o tipo certo…veja qual é tipo polimórfico da exception via depurador…

D

A excpetion a ser capturada não nescessariamente é a primeira ?

ERRO :

WARNING: SQL Error: 1062, SQLState: 23000
SEVERE: Duplicate entry 'a' for key 'PRIMARY'
SEVERE: Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
...
Caused by: java.sql.BatchUpdateException: Duplicate entry 'a' for key 'PRIMARY'
	at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2020)
	at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1451)
	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
	... 81 more
...
WARNING: A system exception occurred during an invocation on EJB PessoaManagerImpl method public void util.commands.comandosManagerImpl.adicionar(java.lang.Object)
javax.ejb.EJBException
	at com.sun.ejb.containers.BaseContainer.processSystemException(BaseContainer.java:5193)
	at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5091)
	at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4879)
....
Caused by: util.exceptions.RegistroDuplicado: Esse Registro  Existe
	at util.commands.comandosManagerImpl.adicionar(comandosManagerImpl.java:31)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
....
Caused by: java.sql.BatchUpdateException: Duplicate entry 'a' for key 'PRIMARY'
	at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2020)
	at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1451)
	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
L

amigo, ddark.emanu vc resolveu seu problema? Pq quero implementar exatamente isso no meu sistema, qdo lançar a exceção ela vir, de uma forma amigável, ao meu usuário final.

C

Bom, nos meus sistemas WEB, que sempre separo nas camadas (pages+)VIEW, SERVICE e DAO(+Model), estou sempre criando exceções genéricas (ViewException, ServiceException e DAOException).

Qualquer exceção que ocorra, por exemplo, na camada DAO, é tratada e relançada como uma DAOException. Acho que a ideia de separar em camadas é justamente separar as responsabilidades. Não quero que as camadas acima (SERVICE e VIEW) precisem tratar problemas específicos a camada DAO, ocorridos nela.
A camada SERVICE tá mandando a camada DAO executar alguma coisa… se der erro/problema, a SERVICE já sabe como tratar, pois a exceção lançada está explícita (checked).

Exemplo, no meu DAO genérico tenho:

public T save(T entity) throws DAOException {
   ...
} catch (ConstraintViolationException e1) {
   ...
   throw new DAOException("Erro de validação.\r\n", e1);
} catch (........) {
} ....

Dessa forma, quem estiver chamando os métodos da camada DAO (seja pela camada SERVICE ou outra que venha a existir futuramente) vai se preocupar só com as exceções que a DAO pode lançar. Posso até no futuro adicionar outros tipos de exceções mais específicas pra DAO (WhateverDAOException, AnotherKindDAOException).

Acho que passar exceções que tiveram origem numa camada, deixando a responsabilidade do tratamento desta exceção pra outras camadas estraga a filosofia da separação de responsabilidades.

Bom… seguindo pra cima, até chegar nos meus ManagedBeans, o que eu faço é…

public void save() {
   ...
} catch (ServiceException e) {
   ...
   FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, mensagem, mensagem);
   FacesContext.getCurrentInstance().addMessage(null, msg);
   ...
}

E assim a o JSF trata, exibindo pro usuário…

Criado 27 de setembro de 2011
Ultima resposta 1 de fev. de 2012
Respostas 64
Participantes 7