Tratamento de Exceptions nos Dao's

Olá, numa aplicação J2EE, é errado tratar exceptions nos DAO’s?? No meu mode ver acho que as exceções deveria ser tratadas pelo controller, pois do controller eu poderia popular os objetos pra view e mostras as exceptions formatadas ao usuário;

O que vocês acham??

Além do mais que tipo de Exception um DAO pode lançar, seria melhor ele lançar JDBCException, ou Exception??

public interface Dao <T> {
	
	public T get(Serializable id) throws JDBCException; //ou throws Exception	
	public void save(T entidade) throws JDBCException;
	public void update(T entidade) throws JDBCException;
	public void delete(T entidade) throws JDBCException;
	public List<T> findAll() throws JDBCException;
	public List<T> findAll(final Integer primeiro, final Integer maxino) throws JDBCException;
}

Att

DAOException parece razoável. JDBCException não. Isto porque está fazendo referencia directa à api JDBC. Um dao pode usar JDBC mas pode usar outras tecnologias tb. Assim a exception não deve ter o nome de nenhuma API. A exceção poderá ser checked ou não conforme o seu gosto. A excção deve encapsular outras exceções internas sem destruir o stacktrace.

[quote=sergiotaborda][quote=danielbussade]
Além do mais que tipo de Exception um DAO pode lançar, seria melhor ele lançar JDBCException, ou Exception??
[/quote]

DAOException parece razoável. JDBCException não. Isto porque está fazendo referencia directa à api JDBC. Um dao pode usar JDBC mas pode usar outras tecnologias tb. Assim a exception não deve ter o nome de nenhuma API. A exceção poderá ser checked ou não conforme o seu gosto. A excção deve encapsular outras exceções internas sem destruir o stacktrace.[/quote]

Olá, o problema de criar uma excessao como DAOException é o seguinte, supondo que tenho o metodo save deste jeito:

[code]
public class DAOException extends Exception{
}

public class UsuarioDao implements Dao<Usuario>{
   private EntityManager em;
   public void save(Usuario entidade) throws DAOException{
         em.persist(entidade); //o que eu faria aqui??? 

        isso???
         try {
	em.persist(entidade);
         } catch (Exception e) {
	throw new DaoException();
       }
     //Ai eu perderia a excessão que realmente ocorreu para tratar no Controller,
    //tipo se ocorreu ConstraintViolationException, era pra tratar e jogar 
   //na tela do usuario Usuario ja cadastrado, mas como eh DAOException 
   //eu não sei o aconteceu realmente


   }
}

Ola,

Entao coloque um try/catch para essa exceçao (ConstraintViolationException) e crie uma expection que a encapsule para relanca-la.

[quote=rodrigo_gomes]Ola,

Entao coloque um try/catch para essa exceçao (ConstraintViolationException) e crie uma expection que a encapsule para relanca-la.[/quote]

Olá rodrigo, obrigado pela dica, pensei em fazer isto, acontece que não sei de antemão quais excessões seriam lancaçadas entendeu??
E criando uma nova exception para encapsular cada uma, poderia acabar com muitas exceptions, entende??

Agora é correto tratar exceptions nos DAO’s?? Acho que não teria o que fazer a não ser relançar uma nova excessão , por isso pensei em colocar algo do tipo throws HibernateException, e tratar tudo no controller, pois é onde eu consigo acesso a view do cliente;
O que você acha??

Obrigado!

Vc nao precisa tratar todas as exceptions que o banco pode lançar. Vc pode colocar try/catch somente para as exceptions que vc quer fazer um tratamento diferente, como mandar uma mensagem diferente para o usuario.

Quanto a tratar exceptions no dao, acho que soh precisa tratar se vc quiser um desacoplamento entre seu dao e quem o chama…ou seja, se vc nao se importa em seu controller saber que seu dao está usando hibernate, faça como vc disse. Ao contrario lance suas proprias exceptions.

[]'s

Ok rodrigo, achei interessante o modo como você disse, agora só mais uma dúvida: Imagine que eu tenha o seguinte cenário:

public class Aluno{
private String matricula;
private String cpf;
}

Os dois são unique keys, ai na minha tela de cadastro quando o aluno tenta cadastrar uma aluno já existente ele vai lancar um constraintViolationException que será tratado e apresentado na tela, a seguinte mensagem.

Aluno ja cadastrado.

A minha dúvida é a seguinte, eu queria saber por qual campo foi violada a integridade, pra mostrar uma mensagem melhor ao usuário:

Tipo CPF já cadastrado
ou
Matricula já cadastrada;

Sabe como resolver isto??

Valeu

Olá.

Você pode criar uma classe responsável por tratar todos as excecoes do sistema, inclusive nas DAOs. Essa classe herdará de Exception:

public class MyException extends Exception{

         public MyException(String mensagem, Throwable causa){
                   super(mensagem, causa);
         }

         public MyException(String mensagem){
                   super(mensagem);
         }
}

Por exemplo, nos métodos das DAOs, que deverão ter o try-catch, você instancia a classe de exceção:

throw new MyException("Ocorreu exceção em tal método");

Essa mensagem será lançada para o usuário. Essa implementação é adequada para os casos que o nosso colega mencionou, mensagem diferentes para cada tipode exceção.

Espero ter ajudado.

Abraço.

Olá , então nos métodos do DAO , eu faria o seguinte:

public class UsuarioDao implements Dao<Usuaro>{

try{
    em.persist(entidade);
    em.flush();
  }catch (Exception e) {
      throw new DAOException(e.getMessage(),e);
    }
}

}

Desta forma, eu não perco a verdadeira excessão que houve, pois no controller eu posso fazer

if(e.getCause instanceof ConstraintViolationException)

///etc......

Agora a minha dúvida é a seguinte, e pra métodos de pesquisa, como :

public List<Usuario> findAll()throws DaoException{

    Query query = em.creeateQuery("from Usuario");
    List<Usuario> usuarios=query.getResultList();
    return usuarios;
}

Aqui, se minha lista volta vazia, é legal eu ter estas validações no DAO e lançar uma exception tipo:

if(list.isEmpty())
 throw new DaoException("Nehum usuario encontrado);

ou

Eu té melhor testar isto no Controller , verificando se a list voltou nula??

Valeu

prefiro essa abordagem.

[quote=rodrigo_gomes][quote=danielbussade]
Eu té melhor testar isto no Controller , verificando se a list voltou nula??
[/quote]

prefiro essa abordagem.[/quote]

As boas práticas dizem que uma lista nunca deve ser nula. O correto seria retornar uma lista vazia! De resto, é a melhor opção mesmo.

Olá, estou fazendo o tratamento de exceptions no DAO’s da seguinte forma:

1- Criei um classe DAOException extends Exception
2- Trato no DAO qualquer excessão e lanço DAOException, sem perder a excessão original desta forma:

public void save(T entidade) throws DAOException {
   try{
	getEm().persist(entidade);
                getEm().flush();
    }catch (Exception e) {
	throw new DAOException(e.getMessage(),e);
         }
 }

3- No Controller, eu faço um instaceof desse jeito:
	try {
	     usuarioDao.save(usuario);
			
	}catch (DAOException e) {
		if(e.getCause() instanceof EntityExistsException){
			addError("nome","Usuário já cadastrado");
			return Constant.ERROR.getRet();
		}

4- Tenho um filtro Global de excessões, que é interceptado em todas actions, 
qualquer excessão que descer na pilha e chegar até ele, é gerado um log, com
hora data, e um stacktrace da excessão em um arquivo .txt, e enviado ao meu e-mail;

Perguntas:

É errado trabalhar assim, fazendo istanceof de excessões??
Quanto a questão do log, existe maneira melhor de fazer??

Gostataria de críticas e sugestões sobre este modo de trabalhar…

Obrigado

Ola Daniel

O Log esta ok! É bom so logar mesmo quando nao for mais lancar a exception, senao é o conhecido log and throw anti pattern.

Sobre a exception, na minha opiniao fazer instanceof nao é uma boa. Alem disso, se a root cause nao for essa que voce esta esperando, (ex: se der uma NullPointerException), voce ta fazendo o catch e simplesmente nao fazendo nada! Ta engolindo a exception… ela vai ocorrer e voce nem vai ficar sabendo. A pagina do jsp vai aparecer normalmente, nenhum log, nenhuma mensagem sera apresentada.

[quote=Paulo Silveira]Ola Daniel

O Log esta ok! É bom so logar mesmo quando nao for mais lancar a exception, senao é o conhecido log and throw anti pattern.

Sobre a exception, na minha opiniao fazer instanceof nao é uma boa. Alem disso, se a root cause nao for essa que voce esta esperando, (ex: se der uma NullPointerException), voce ta fazendo o catch e simplesmente nao fazendo nada! Ta engolindo a exception… ela vai ocorrer e voce nem vai ficar sabendo. A pagina do jsp vai aparecer normalmente, nenhum log, nenhuma mensagem sera apresentada.[/quote]

Obrigado Paulo pelas dicas, eu esqueci de postar o resto na verdade eu faço assim:

try {   
         usuarioDao.save(usuario);   
               
    }catch (DAOException e) {   
        if(e.getCause() instanceof EntityExistsException){   
            addError("nome","Usuário já cadastrado");   
            return Constant.ERROR.getRet();   
        }   
      addError(Tools.getInitCause(e).getMessage());
      return Constant.ERRO.getRet();

Esse método getInitCause, pega a causa inicial de Exception que está encpasulada dentro de DaoException enquanto ela for diferente de null, ele vai descendo até chegar na causa inicial:

Gostaria até de saber se já existe algum método para isso??

Outra coisa como você aconselheria a fazer, para verificar se o usuário já está cadastrado no sistema, e apresentar para o usuário na tela??

Obrigado

[quote=danielbussade]
Olá, o problema de criar uma excessao como DAOException é o seguinte, supondo que tenho o metodo save deste jeito:

[code]
public class DAOException extends Exception{
}

public class UsuarioDao implements Dao{
private EntityManager em;
public void save(Usuario entidade) throws DAOException{
em.persist(entidade); //o que eu faria aqui???

    isso???
     try {
em.persist(entidade);
     } catch (Exception e) {
throw new DaoException();
   }
 //Ai eu perderia a excessão que realmente ocorreu para tratar no Controller,
//tipo se ocorreu ConstraintViolationException, era pra tratar e jogar 

//na tela do usuario Usuario ja cadastrado, mas como eh DAOException
//eu não sei o aconteceu realmente

}
}
[/code][/quote]

O seu problema é entender como trabalhar com exeções.
Para começar leia isto

como ficaria o codigo


public ConstraintViolationDAOException  extends DAOException {}

 public void save(Usuario entidade) throws DAOException{
         
          if (entidade==null){
                 return;
         } 
         try {
	    em.persist(entidade);
         } catch (ConstraintViolationException e){
            throw new ConstraintViolationDAOException (e)
         } catch (Exception e) {
	    throw new DaoException(e);
       }

   }

Num DAO não ha tratamento nenhum para o usuário final. É expressamente proibida a utilização de funções de comunicação com o usuário final, tal como objetos Swing por exemplo.
Vc tem que separar as exeções que recebe da API mais baixa para exceções da API mais alta. às vezes é 1:1, às vezes não. As que vc não souber deparar vc coloca numa exceção generica. É o caro que chamar o DAO que vai tratar as execções e comunicar com o usuário se for necessário. (na realidade isso só deve acontecer na camada GUI)

Na minha opinião seria melhor DAOException herdade de RuntimeException,caso contrario vc vai ter muito trabalho. Mas isso é a minha opinião.

Cuidado com exceções especias como NullPointerException. Sempre verifique que as coisas não são null antes das usar.

Obrigado sérgio pelas dicas, só mais uma é certo chegar se a entidade é null no DAo?? Ou ela deveria ser checada antes de chegar no DAO, resumindo, tem algum problema em fazer lógica relacionada ao banco no DAO como isto por exemplo??

  if (entidade==null){   
                 return;   
         }   

Att

[quote=danielbussade]Obrigado sérgio pelas dicas, só mais uma é certo chegar se a entidade é null no DAo?? Ou ela deveria ser checada antes de chegar no DAO, resumindo, tem algum problema em fazer lógica relacionada ao banco no DAO como isto por exemplo??

  if (entidade==null){   
                 return;   
         }   

[/quote]

Isso não é logica relacionada ao banco é validação de parametros. Todo o método bem construido tem que verificar os parametros conforme o contrato que estipulou na assinatura. No caso eu simplesmente não fiz nada, mas poderia ser enviada uma IllegalArgumentException.

Não importa por quantas camadas o objeto passe , importa que em cada ponto o contrato do método seja validado. Se vc fizer isso na camada superior tanto melhor, mas vc não pode assumir que isso foi feito. Nenhuma camada pode assumir nada sobre a sua antecessora. Por isso a validação tem que acontecer em todos os niveis. Isso não significa que o tratamento seja igual em todas as camadas.

Excelente artigo!!

Daniel, aqui tem mais um, com as boas praticas citadas pelo Sergio tambem, mas nao tao completo:

[quote=sergiotaborda][quote=danielbussade]Obrigado sérgio pelas dicas, só mais uma é certo chegar se a entidade é null no DAo?? Ou ela deveria ser checada antes de chegar no DAO, resumindo, tem algum problema em fazer lógica relacionada ao banco no DAO como isto por exemplo??

  if (entidade==null){   
                 return;   
         }   

[/quote]

Isso não é logica relacionada ao banco é validação de parametros. Todo o método bem construido tem que verificar os parametros conforme o contrato que estipulou na assinatura. No caso eu simplesmente não fiz nada, mas poderia ser enviada uma IllegalArgumentException.

Não importa por quantas camadas o objeto passe , importa que em cada ponto o contrato do método seja validado. Se vc fizer isso na camada superior tanto melhor, mas vc não pode assumir que isso foi feito. Nenhuma camada pode assumir nada sobre a sua antecessora. Por isso a validação tem que acontecer em todos os niveis. Isso não significa que o tratamento seja igual em todas as camadas.[/quote]

Olá realmente é melhor não se basear em suposições de que os dados já viram corretos, pois se vier nulo ocorre um risco de NullPointerException.

Obrigado.