How to handle database exceptions with JPA? And, how to use annotations in place of the IMessage interface, as below?
Example:
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/programadorthi.