Hibernate gestion des exceptions
J'ai un peu "complexe" de la question.
Je suis en utilisant Hibernate/JPA, à effectuer des transactions avec un DB.
Je ne suis pas l'administrateur de la base, et un client consomme ma demande, un service web RESTful. Mon problème est que la DB est altérée (pas très souvent, mais il est toujours en évolution). Aussi, le client ne respecte pas toujours l'entrée de mon application (longueur, type, etc.). Lorsque cela arrive, Hibernate génère une exception. L'exception est difficile à traduire et lire le journal, parce qu'il a des exceptions imbriquées et se compose de beaucoup de texte: comme je l'ai dit, très difficile à comprendre.
Je veux savoir si il est possible de gérer les exceptions sur au niveau de l'entité, en jetant peut-être une exception personnalisée.
Je les remercie de votre patience et de votre aide à l'avance.
EDIT:
Au total, j'ai réussi à faire ce que je voulais, ne sais pas si c'est fait le droit chemin.
App.java
package com.mc;
import org.hibernate.Session;
import com.mc.stock.Stock;
import com.mc.util.HibernateUtil;
import javax.persistence.EntityManager;
public class App {
public static void main(String[] args) {
Set<ConstraintViolation<Stock>> violations;
validator = Validation.buildDefaultValidatorFactory().getValidator();
Scanner scan = new Scanner(System.in);
EntityManager em = null;
System.out.println("Hibernate one to many (Annotation)");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Stock stock = new Stock();
String nextLine = scan.nextLine();
stock.setStockCode(nextLine.toString());
nextLine = scan.nextLine();
stock.setStockName(nextLine.toString());
violations = validator.validate(stock);
if (violations.size() > 0) {
StringBuilder excepcion = new StringBuilder();
for (ConstraintViolation<Stock> violation : violations) {
excepcion.append(violation.getMessageTemplate());
excepcion.append("\n");
}
System.out.println(excepcion.toString());
}
session.save(stock);
session.getTransaction().commit();
}
}
FieldMatch.java
package com.mc.constraints;
import com.mc.constraints.impl.FieldMatchValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = FieldMatchValidator.class)
@Documented
public @interface FieldMatch {
String message() default "{constraints.fieldmatch}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String first();
String second();
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
FieldMatch[] value();
}
}
FieldMatchValidator.java
package com.mc.constraints.impl;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.mc.constraints.FieldMatch;
import org.apache.commons.beanutils.BeanUtils;
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
private String firstFieldName;
private String secondFieldName;
@Override
public void initialize(final FieldMatch constraintAnnotation) {
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
try {
final Object firstObj = BeanUtils.getProperty(value, firstFieldName);
final Object secondObj = BeanUtils.getProperty(value, secondFieldName);
return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
} catch (final Exception ignore) {
//ignore
}
return true;
}
}
Stock.java
package com.mc.stock;
import com.mc.constraints.FieldMatch;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.validator.constraints.Length;
@Entity
@Table(name = "STOCK")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "Stock.findAll", query = "SELECT s FROM Stock s"),
@NamedQuery(name = "Stock.findByStockId", query = "SELECT s FROM Stock s WHERE s.stockId = :stockId"),
@NamedQuery(name = "Stock.findByStockCode", query = "SELECT s FROM Stock s WHERE s.stockCode = :stockCode"),
@NamedQuery(name = "Stock.findByStockName", query = "SELECT s FROM Stock s WHERE s.stockName = :stockName")})
@FieldMatch.List({
@FieldMatch(first = "stockCode", second = "stockName", message = "Code and Stock must have same value")
})
public class Stock implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_stock_id")
@SequenceGenerator(name = "seq_stock_id", sequenceName = "seq_stock_id", initialValue = 1, allocationSize = 1)
@Basic(optional = false)
@Column(name = "STOCK_ID", unique = true, nullable = false)
private Integer stockId;
@Column(name = "STOCK_CODE")
private String stockCode;
@Length(min = 1, max = 20, message = "{wrong stock name length}")
@Column(name = "STOCK_NAME")
private String stockName;
public Stock() {
}
public Stock(Integer stockId) {
this.stockId = stockId;
}
public Integer getStockId() {
return stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
public String getStockCode() {
return stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
public String getStockName() {
return stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
@Override
public int hashCode() {
int hash = 0;
hash += (stockId != null ? stockId.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
//TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Stock)) {
return false;
}
Stock other = (Stock) object;
if ((this.stockId == null && other.stockId != null) || (this.stockId != null && !this.stockId.equals(other.stockId))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.mc.stock.Stock[ stockId=" + stockId + " ]";
}
}
HibernateUtil.java
package com.mc.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
//Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
//Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
//Close caches and connection pools
getSessionFactory().close();
}
}
Oracle DB Structure
CREATE TABLE stock
(
STOCK_ID NUMBER(5) NOT NULL ,
STOCK_CODE VARCHAR2(10) NULL ,
STOCK_NAME VARCHAR2(20) NULL
);
ALTER TABLE stock
add CONSTRAINT PK_STOCK_ID PRIMARY KEY (STOCK_ID);
create sequence seq_stock_id
start with 1
increment by 1
nomaxvalue;
Vous devez vous connecter pour publier un commentaire.
Je suis enclin à faire autant de validation avant d'obtenir le niveau de DB. Jetez un oeil à Hibernate Validator http://www.hibernate.org/subprojects/validator.html qui est la référence pour l'implémentation de la JSR-303.
L'aide d'annotations standard, vous pouvez appliquer les contraintes et d'obtenir de bons messages d'erreur avant de tenter de mettre les entités dans votre base de données.
Je crois que cela vous permettra de valider au niveau de l'entité, comme demandé.
Je ne suis pas sûr de ce que tu veux dire à propos de "au niveau de l'entité", mais bien sûr. Mettre un try/catch autour du code qui est en invoquant mise en veille prolongée. Catch Throwable et renvoyer tout ce que vous voulez. L'astuce est de mettre un peu de raison qui fait sens dans l'exception que vous êtes jeter.
Bien sûr, un point important est que vous devez valider tous les commentaires.
Vous pouvez implémenter votre propre Sqlexceptionconverter qui est et de les traiter de la façon dont vous le souhaitez.
Utiliser la propriété " mise en veille prolongée.jdbc.sql_exception_converter " pour définir votre convertisseur personnalisé.
Je suis incapable de trouver plus de documentation, vous avez besoin de creuser dans les implémentations par Hibernate pour en savoir plus.
Par ailleurs, pourquoi ne pouvez-vous pas avoir un filtre global, qui attrape tous les exception et de décider quelle exception de re lancer comme il est ou lancer une nouvelle exception? Vous allez faire plus ou moins la même, même si vous implémentez votre propre sqlexceptionconverter qui est.
d'après mon expérience, vous devez attraper la SQLException, et alors u peut obtenir facilement le code d'erreur SQL pour la base de données spécifique.
Par exemple: votre base de données est mysql et u obtenu le code d'erreur 1062 . Ainsi, vous pouvez savoir que l'erreur est Dupliqué erreur de saisie. Vous pouvez vérifier le code d'erreur mysql
http://www.briandunning.com/error-codes/?source=MySQL