Lançar Exception... boa prática?

Salve Gujeiros.

Estou com algumas dúvidas com relação ao lançamento de exception para exibição de mensagens ao usuário… espero que possam me ajudar :slight_smile:

[code]
public class ComumLogic {

public String salvaEntidade(Object obj) throws EntityExistsException, Exception{
	logger.log(Level.DEBUG, "salvando obj: " + obj);
	String ret = "nok";
	try{
		em.persist(obj);
		ret = "ok";
	}
	catch(EntityExistsException e){
		throw new EntityExistsException("O objeto já existe.", e);
	}
	catch(Exception e){
		throw new Exception("Erro ao persistir objeto.", e);
	}
	
	return ret;
}

public String alteraEntidade(Object obj) throws Exception{
	logger.log(Level.DEBUG, "alterando obj: " + obj);
	String ret = "nok";
	try{
		em.merge(obj);
		ret = "ok";
	}
	catch(Exception e){
		throw new Exception("Erro ao alterar objeto.", e);
	}
	
	return ret;
}

public String excluiEntidade(Object id) throws RollbackException, Exception{
	String ret = "nok";
	
	if(logger.isDebugEnabled())
		logger.log(Level.DEBUG, "excluindo obj: " + ReflectionTools.showAllAttributes(id));

	try{
		id = em.merge(id);
		em.remove(id);
		em.getTransaction().commit();
		ret = "ok";
	}
	catch(RollbackException e){
		String message = "Erro ao excluir objeto";
		if (e.getCause() instanceof ConstraintViolationException)
			message = "A exclusão do objeto viola restrição de chave estrangeira.";
		throw new RollbackException(message, e); 
	}
	catch(Exception e){
		throw new Exception("Erro ao excluir objeto.", e);
	}
		
	return ret;
}

}[/code]

A intenção seria que caso ocorra um Exception, essa Exception seja lançada até a classe Controller, que seria responsável por exibir a mensagem da Exception ao usuário e fazer o log…

Por exemplo: No caso de tentar excluir um objeto que é referenciado como chave estrangeira em outra Entidade, seria lançada uma ConstraintViolationException e este erro deveria ser informado ao usuário.

Seria uma boa prática ao invés de lançar vários tipos de Exceptions (ConstraintViolationException, EntityExistsdException) lançar apenas Exception?

Ex.

public String excluiEntidade(Object id) throws Exception{
		String ret = "nok";
		
		if(logger.isDebugEnabled())
			logger.log(Level.DEBUG, "excluindo obj: " + ReflectionTools.showAllAttributes(id));

		try{
			id = em.merge(id);
			em.remove(id);
			em.getTransaction().commit();
			ret = "ok";
		}
		catch(RollbackException e){
			String message = "Erro ao excluir objeto";
			if (e.getCause() instanceof ConstraintViolationException)
				message = "A exclusão do objeto viola restrição de chave estrangeira.";
			throw new RollbackException(message, e); 
		}
		catch(Exception e){
			throw new Exception("Erro ao excluir objeto.", e);
		}
			
		return ret;
	}

Esta seria a melhor maneira de tratar a exception e exibir o erro ao usuário?

Quem puder ajudar… agradeço!

[]'s

Lançar Exception é uma péssima prática…

Lance exceções específicas e se preferir faça um catch(Exception e), mas nunca lance Exception.

Para exceções de negócio, normalmente crio uma BusinessException, capturo no controller e exibo em tela…

[quote=ignacio83]Lançar Exception é uma péssima prática…

Lance exceções específicas e se preferir faça um catch(Exception e), mas nunca lance Exception.

Para exceções de negócio, normalmente crio uma BusinessException, capturo no controller e exibo em tela…[/quote]

valeu pela dica ignacio…

No caso o que está me deixando na dúvida é o seguinte:

Ao utilizar JPA/Hibernate ele poderá lançar algumas exceções como ConstraintViolationException, EntityExistsException , entre outras…

No caso eu teria que descobrir todas as Exceptions possíveis para lançar cada uma delas?

Ex.

public String salvaEntidade(Object obj) throws ConstraintViolationException, IllegalArgumentException, OutraException, MaisumaException{  

Ou poderia fazer algo do tipo:

public String salvaEntidade(Object obj) throws MinhaException{ logger.log(Level.DEBUG, "salvando obj: " + obj); String ret = "nok"; try{ em.persist(obj); ret = "ok"; } catch(EntityExistsException e){ throw new MinhaException("O objeto já existe.", e); } catch(Exception e){ throw new MinhaException("Erro ao persistir objeto.", e); } }
Já que neste caso estaria colocando a mensagem que gostaria que fosse exibida ao usuário e também passando a exceção que será lançada no log…

desculpem a ignorância mais é que ainda não assimilei muito bem a idéia… hehe

quem tiver paciência e puder ajudar :smiley:

[]'s

Perfeito

[quote=ignacio83][quote]

   1. public String salvaEntidade(Object obj) throws MinhaException{    
   2.          logger.log(Level.DEBUG, "salvando obj: " + obj);    
   3.         String ret = "nok";    
   4.          try{    
   5.              em.persist(obj);    
   6.             ret = "ok";    
   7.          }    
   8.          catch(EntityExistsException e){    
   9.              throw new MinhaException("O objeto já existe.", e);    
  10.          }    
  11.          catch(Exception e){    
  12.              throw new MinhaException("Erro ao persistir objeto.", e);    
  13.          }    
  14. }  

[/quote]

Perfeito[/quote]

valeu ignacio83! :thumbup:

Voce pode criar classes para gerar excecoes personalizadas. :wink:

leia os artigos do sergio sobre exceptions, na sequencia q coloquei aqui…, são extremamente uteis

Se vc esta usando algo que lance diversas exceções, e vc esta programando um trecho do programa resposnsável por isso, o correto é vc identificar o erro…

e mastiga-lo, a partir do erro identificado, encapsular em uma Exception conhecida, bem especifica, com o maximo de informações possiveis, e lançar essa exceção completa pra cima…

fazendo isso, vc vai estar garantido, que quando for agarrar la em cima essa exceção, vai ter todas as informações suficientes para dar o devido tratamento a ela…

catch(Exception e){ throw new MinhaException("Erro ao persistir objeto.", e); }

isso aqui não é bom… conceitualmente esta correto, mas na pratica não esta…

fazer

[code]catch(Exception e){

}[/code]

não faz sentido … ate pq vc esta agarrando algo que não conhece, pois se conhecesse não seria (Exception e), e sim algo como (FileNotFoundException e)

se vc não conhece o que esta vindo, não tem como tratar, se não tem como tratar, ou nem como polir melhor a informação para enviar a cima, vc so vai dificultar o seu trabalho fazendo algo como… MinhaException.getCause() q retorna “e” … pois teu codigo vai ficar cheio de MinhaEception, que na realidade não quer dizer nada, não tem informação nenhuma, não é especifica, e não vai te ajudar a ter multiplos catchs pra tratala… pense nisso

faça catchs, seguros… segurar (Exception e) é um ERRO!, de estrategia, mas é um erro

sobre esse trecho…

try{ em.persist(obj); ret = "ok"; } catch(EntityExistsException e){ throw new MinhaException("O objeto já existe.", e); }

c vc quer transformar EntityExcistException em uma exceção sua, isso só fará real sentido se sua exceção for mais especifica do que EntityExistException… por exemplo

try{ em.persist(obj); ret = "ok"; } catch(EntityExistsException e){ //suponha que, MinhaEntityExistException extends MinhaException throw new MinhaEntityExistException ("O objeto já existe.",obj, e); }

agora vc esta um pouco mais especialista na msg… esta enviando uma msg de erro, um objeto junto, e a Exception causa do problema…

poderia adicionar mais informação, por exemplo, qual o campo relevante para que seja conciderado Duplicador o objeto, e assim essa informação pode ser usada para informar ao usuario que campo ele deve Alterar, para a exceção sumir, e ele conseguir persist, por exemplo

throw new MinhaEntityExistException("O objeto ja existe",obj,e,"cpf");

ai o quem catasse e esceção poderia verificar

e.getMessage(); e.getEntity(); e.getCause(); e.getDuplicateField();

viu ? como vc efetivamente esta passando mais informações ?? sem falar que agora a propria exceção é especifica do Problema “MinhaEntityExistException” e não deixou de ser uma “MinhaException” ja que extende essa…

e assim quando alguem agarrar pode estar seguro de estar agarrado algo com informações suficiente, para resolver o problema…

por exemplo, quem agarrar pode enviar para uma outra tela

toEditScreen(e.getEntity(),e.getDuplicateField());

e nessa nova tela, o campo duplicado seria realçado como o problema a ser resolvido pelo usuario

Lavieri,

obrigado pelos esclarecimentos e pelas dicas…

já havia visto um desses artigos do Sergio Taborda e são realmente muito bons!

vou dar uma estudada melhor aqui e tentar melhorar esse tratamento das Exceptions. :thumbup:

[]'s

bom moçada… me surgiu uma outra dúvida hehe

No sistema existem duas entidades onde uma entidade referencia a outra como chave estrangeira.

Quando o usuário tenta excluir um registro da tabela e esse registro possui sua chave referenciada na outra tabela é lançada a seguinte Exception:

Caused by: javax.persistence.RollbackException: Error while commiting the transaction
	at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
	at br.com.fertimitsui.comum.bean.ComumLogic.excluiEntidade(ComumLogic.java:71)
	.. 22 more
Caused by: 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)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:249)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)

A exception lançada é a RollbackException, que é lançada quando ocorre um erro no commit.

[quote]public class RollbackException
extends PersistenceException

Thrown by the persistence provider when the EntityTransaction.commit() fails. [/quote]

A única forma que encontrei para descobrir se ocorreu uma ConstraintViolationException foi essa:

try{ id = em.merge(id); em.remove(id); em.getTransaction().commit(); ret = "ok"; } catch (RollbackException e){ if (e.getCause() instanceof ConstraintViolationException){ ConstraintViolationException ce = (ConstraintViolationException) e.getCause(); String message = "A exclusão do registro viola restrição de chave estrangeira."; throw new ConstraintViolationException(message, ce.getSQLException(), ce.getConstraintName()); } throw new RollbackException("Erro ao remover registro.", e); }

Ou seja, fui “obrigado” a capturar uma exceção (RollbackException) que não tenho como tratar (um commit pode falhar por vários motivos… certo?) apenas para descobrir se ocorreu uma ConstraintViolationException…

Neste caso, a solução adotada é correta?

desculpem se eu estiver viajando… :XD: hehe

[]'s

depende… se pra vc, saber que foi um Rollback, originado por uma Constratin não for interessante …

o fato é que no fim vc vai estar perdendo informação, estja bastante seguro caso va fazer isso, e aconselho no minimo, ja que esta criando uma nova ConstratintViolationException, colocar a sua causa na criação, afinal de contas, ela foi causada pela RollbackException ^^, ai pelomenos vc estará se assegurando que a informação não é perdida…

e + … se trabalhar com ConstraintViolationException, não for suficiente, crie a sua propria… na sua propria vc pode enviar mais coisa, se assim for necessario… não li as especificações de ConstraintViolation, mas talvés ele não faça referencia a entidade que causou o erro, e nesse ponto onde vc esta fazendo o Catch… vc pode adcionar a informação da entidade… e quanto + informações na exception melhor…

e existe um padrão que o Sergio comenta nakeles artigos que te passei… esse padrão seria algo como…

try{ id = em.merge(id); em.remove(id); em.getTransaction().commit(); ret = "ok"; } catch (RollbackException e){ throw new HandleRollbackException.handle(e); //é algo + ou - assim... não lembro exatamente }

ai essa classe Handle, saberia como destrinchar a exceção do tipo Rollback, e transformar em uma exceção mais informativa…

mais uma vez obrigado Lavieri!

aos poucos vou assimilando tudo… hehe
:thumbup:

[]'s

[code]public class ComumLogic {

public String salvaEntidade(Object obj) throws RepositoryException {
	logger.log(Level.DEBUG, "salvando obj: " + obj);
	String ret = "nok";
	try{
		em.persist(obj);
		em.flush();
		em.getTransaction().commit();
		ret = "ok";
	}
	catch(EntityExistsException e){
		if (em.getTransaction() != null && em.getTransaction().isActive())
			em.getTransaction().rollback();
		throw new RegistroDuplicadoException("O registro já existe. O campo de identificação do registro não pode ser duplicado.", obj, e);
	}
	catch (RollbackException e){
		throw new RollbackExceptionHandler().handle(e, obj);
	}
	return ret;
}

public String alteraEntidade(Object obj){
	logger.log(Level.DEBUG, "alterando obj: " + obj);
	String ret = "nok";
	em.merge(obj);
	ret = "ok";
	return ret;
}

public String excluiEntidade(Object obj) throws RepositoryException {
	String ret = "nok";
	
	if(logger.isDebugEnabled())
		logger.log(Level.DEBUG, "excluindo obj: " + ReflectionTools.showAllAttributes(obj));

	try{
		obj = em.merge(obj);
		em.remove(obj);
		em.getTransaction().commit();
		ret = "ok";
	}
	catch (RollbackException e){
		if (em.getTransaction() != null && em.getTransaction().isActive())
			em.getTransaction().rollback();
		throw new RollbackExceptionHandler().handle(e, obj);
	}
	return ret;
}

}[/code]

RepositoryException (extends RuntimeException) é a “mãe” de todas as outras Exceptions (RegistroDuplicadoException, ViolacaoChaveEstrangeiraException)…
RollbackExceptionHandler trata uma RoolbackException e verifica se a causa foi uma ConstraintViolationException…

a idéia seria mais ou menos essa Lavieri??

valeu! :smiley:
[]'s