Hibernateexception处理

我有一个“复杂”的问题。

我正在使用Hibernate / JPA与DB进行交易。

我不是DBA,客户端使用我的应用程序,一个RESTful Web服务。 我的问题是数据库被改变了(不经常,但它仍然改变)。 此外,客户端并不总是尊重我的应用程序的输入(长度,类型等)。 当发生这种情况时,Hibernate会抛出exception。 exception很难从日志中翻译和读取,因为它有嵌套exception并且包含大量文本:就像我说的那样,很难理解。

我想知道是否可以在实体级别处理exception,抛出可能是自定义的exception。

我感谢你的耐心和帮助。

编辑:

Fianlly我设法做了我想做的事,不确定它是否以正确的方式完成。

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> 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 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[] 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 { 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结构

 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; 

在获得数据库级别之前,我倾向于进行尽可能多的validation。 看看Hibernate Validator, http: //www.hibernate.org/subprojects/validator.html,这是JSR-303的参考实现。

使用标准注释可以在尝试将实体放入数据库之前强制执行约束并获取良好的错误消息。

我相信这将允许您按要求在实体级别进行validation。

我不确定你对“实体级别”的意思,但肯定。 在调用Hibernate的代码周围放一个try / catch。 抓住Throwable并重新抛出任何你想要的东西。 诀窍是把一些理由放在你抛出的exception中。

当然,一个重点是你应该validation所有输入。

您可以实现自己的SQLExceptionConverter并以您希望的方式处理它。

使用属性’hibernate.jdbc.sql_exception_converter’设置自定义转换器。

我无法找到更多这方面的文档,你需要深入了解Hibernate的实现以找到更多。

顺便说一句,为什么你不能有一个全局filter,它捕获每个exception并决定抛出哪个exception或抛出一个新exception? 即使您实现自己的SQLExceptionConverter,您也会或多或少地做同样的事情。

根据我的经验,你应该捕获SQLException,然后你可以轻松获得特定数据库的SQL错误代码。 例如:你的数据库是mysql,你得到错误代码1062。 所以你可以知道错误是重复的输入错误。 您可以检查mysql错误代码http://www.briandunning.com/error-codes/?source=MySQL