1
How to handle database exceptions with JPA? And, how to use annotations instead of the Imessage interface, as below?
Example:
- My database returns error: "Violation Constraint uk_email";
- Pick up the error message Constraint (uk_email) and look in the same database or in another message corresponding to it.
My solution so far:
Interface
package br.com.handlingjpa; /** * Interface usada para identificar qual é a classe de entidade que * representa a tabela de mensagens * * @author Thiago Santos <[email protected]> * */ public interface IMessage { /** * Obté o nome da constraint encontrada * * @return A constraint encontrada */ String getConstraint(); /** * Defini qual foi a constraint encontrada na exceçã * * @param constraint O nome da constraint */ void setConstraint(String constraint); /** * Obté a mensagem referente a contraint * * @return A mensagem */ String getMessage(); /** * Determina qual é a mensagem referente a constraint * * @param message A mensagem */ void setMessage(String message); /** * Obté a expressão regular que determina como encontrar uma * constraint * * @return A expressão regular */ String getRegex(); }
Handling Exception class
/* * Copyright (c) 2014. All rights reserved. */ package br.com.handlingjpa; import java.sql.SQLException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.EntityManager; import javax.persistence.Query; /** * <p> * A classe <code>HandlingDBException</code> é utilizada para tratar * erros gerado pela base de dados. A mesma busca na exceção (a * resposta dada pela base de dados do ocorrido) por um identificador do erro. * Ao encontrar-lo, a classe realiza uma consulta em busca da mensagem * correspondente. * </p> * * @author Thiago Santos <[email protected]> * */ public class HandlingDBException { /** * A classe de entidade que faz referência a tabela de mensagens */ private IMessage entityClass; /** * O nome da <code>NamedQuery</code> usada para consulta */ private String namedQuery; /** * O nome do parâmetro na <code>NamedQuery</code> usado como filtro */ private String paramName; /** * Cria um HandlingDBException e determina quem é a classe de * entidades, o nome da NamedQuery e o identificador do parâmetro na * NamedQuery * * @param entityClass A <b>instância</b> da classe de entidade. Ex: <code>new Entidade()</code> * @param namedQuery O nome da NamedQuery de consulta * @param paramName O identificador do parâmetro na NamedQuery */ public HandlingDBException(IMessage entityClass, String namedQuery, String paramName) { this.entityClass = entityClass; this.namedQuery = namedQuery; this.paramName = paramName; } /** * Busca uma mensagem com base em um erro enviado pela base de dados. Caso * encontre, será adicionado a entidade a constraint encontrada e a * mensagem correspondente a ela. Caso contrário, será * retornado a entidade do jeito que foi passada no construtor * * @param exception Um Throwable com a exceção * @param connection A conexão que diz onde executar a consulta * @return Retorna a própria entidade passada no construtor */ public IMessage getMessageFromDatabase(Throwable exception, EntityManager connection) { // Busca por uma SQLException ou ate que seja null while (exception != null && !(exception instanceof SQLException)) { exception = exception.getCause(); } // Verifica se e uma SQLException if (exception instanceof SQLException) { SQLException ex = (SQLException) exception; // Monta a regex de constraint Pattern pattern = Pattern.compile(entityClass.getRegex()); // Procura a ocorrencia na mensagem de erro Matcher matcher = pattern.matcher(ex.getMessage()); // Verifica se achou alguma constraint if (matcher.find()) { // Obtem a constraint no meio da mensagem String constraint = ex.getMessage().substring(matcher.start(), matcher.end()); entityClass.setConstraint(constraint); // Busca a mensagem na base de dados Query query = connection.createNamedQuery(namedQuery); query.setParameter(paramName, constraint); String message = (String) query.getSingleResult(); entityClass.setMessage(message); } } return entityClass; } }
Entity Class
package br.com.handling.modelo; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.NamedNativeQuery; import javax.persistence.NamedQuery; import javax.persistence.Table; import br.com.handlingjpa.IMessage; @Entity @Table(name = "tb_mensagem") @NamedNativeQuery( name = "findMensagemFunction", query = "SELECT dbo.uf_buscar_mensagem(:constraint)") @NamedQuery( name = "findMensagemQuery", query = "SELECT m.message FROM Mensagem m WHERE m.constraint = :constraint") public class Mensagem implements IMessage { @Id @Column(name = "tx_constraint") private String constraint; @Column(name = "tx_mensagem") private String message; public Mensagem() { } @Override public String getConstraint() { return this.constraint; } @Override public void setConstraint(String constraint) { this.constraint = constraint; } @Override public String getMessage() { return this.message; } @Override public void setMessage(String message) { this.message = message; } @Override public String getRegex() { return "(?i)([pfuc]k_\\w+)"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((constraint == null) ? 0 : constraint.hashCode()); return result; } @Override public boolean equals(Object obj) { if (obj instanceof Mensagem) { Mensagem msg = (Mensagem) obj; if (msg.getConstraint() != null) { return msg.getConstraint().equals(this.getConstraint()); } } return false; } @Override public String toString() { return this.getMessage(); } }
Main class
public static void main(String[] args) { EntityManager em = // A conexão Teste t = new Teste(); t.setId(2); t.setEmail("[email protected]"); try { em.getTransaction().begin(); em.persist(t); em.getTransaction().commit(); } catch (PersistenceException pex) { // Tratando a exceção e buscando a mensagem usando SELECT HandlingDBException hdbe = new HandlingDBException(new Mensagem(), "findMensagemQuery", "constraint"); System.out.println(">>>> Mensagem: " + hdbe.getMessageFromDatabase(pex, em).getMessage()); // Realizando log pex.printStackTrace(); } finally { // Fechar conexão } }
More details are on my github.com/programmerthi.
I don’t know any kind of framework in that sense, @Ascension. I’ve seen exceptions treatment by message snippet as you want, but all "done in the nail" even, nothing ready.
– Luiz Fernando Bueno
@Luizfernandobueno appreciated the answer. The question is that I had to create the sources of the links above and it was complicated. If there was a framework it would make my :D side easier
– Ascension