java 休眠异常处理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7707936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Hibernate exception handling
提问by Marco Aviles
I've got a little 'complex' question.
我有一个有点“复杂”的问题。
I'm using Hibernate/JPA to make transactions with a DB.
我正在使用 Hibernate/JPA 与数据库进行交易。
I'm not the DBA, and a client consumes my application, a RESTful web service. My problem is that the DB is altered (not very often, but it still changes). Also, the client does not always respect input for my application (length, type, etc.). When this happens Hibernate throws an exception. The exception is difficult to translate and read from the log, because it has nested exceptions and consists of a lot of text: like I said, very difficult to understand.
我不是 DBA,客户端使用我的应用程序,一个 RESTful Web 服务。我的问题是数据库被改变了(不是很频繁,但它仍然在改变)。此外,客户端并不总是尊重我的应用程序的输入(长度、类型等)。当这种情况发生时,Hibernate 会抛出异常。异常难以翻译和从日志中读取,因为它具有嵌套异常并且由大量文本组成:就像我说的,非常难以理解。
I want to know if it's possible to handle exceptions on entity level, throwing maybe a customized exception.
我想知道是否可以在实体级别处理异常,抛出自定义异常。
I thank your patience and help in advance.
我提前感谢您的耐心和帮助。
EDIT:
编辑:
Fianlly I managed to do what I wanted, not sure if it's done the right way.
最后,我设法做我想做的事,不确定它是否以正确的方式完成。
App.java
应用程序.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
字段匹配验证器.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
股票.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
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
Oracle 数据库结构
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;
回答by James DW
I'm inclined to do as much validation before you get the the DB level. Have a look at Hibernate Validator, http://www.hibernate.org/subprojects/validator.htmlwhich is the reference implementation of JSR-303.
在您获得 DB 级别之前,我倾向于进行尽可能多的验证。看看 Hibernate Validator,http://www.hibernate.org/subprojects/validator.html,它是 JSR-303 的参考实现。
Using standard annotations you can enforce constraints and get good error messages before you attempt to put the entities into your database.
使用标准注释,您可以在尝试将实体放入数据库之前强制执行约束并获得正确的错误消息。
I believe this will allow you to validate at the entity level as requested.
我相信这将允许您根据要求在实体级别进行验证。
回答by John B
I am not sure what you mean about "entity level", but sure. Put a try/catch around the code that is invoking Hibernate. Catch Throwable and rethrow whatever you want. The trick is putting some reason that makes sense in the exception you are throwing.
我不确定您对“实体级别”的意思,但可以肯定。在调用 Hibernate 的代码周围放置一个 try/catch。抓住 Throwable 并重新抛出任何你想要的东西。诀窍是在您抛出的异常中加入一些有意义的原因。
Of course, one major point is that you should validate all input.
当然,主要的一点是您应该验证所有输入。
回答by Adisesha
You can implement your own SQLExceptionConverterand handle it the way you want.
您可以实现自己的SQLExceptionConverter并按照您想要的方式处理它。
Use the property 'hibernate.jdbc.sql_exception_converter' to set your custom converter.
使用属性“hibernate.jdbc.sql_exception_converter”来设置您的自定义转换器。
I am unable to find more documentation this, you need to dig into implementations by Hibernate to find more.
我无法找到更多文档,您需要深入研究 Hibernate 的实现以找到更多。
By the way, why can't you have a global filter, which catches every exception and decide which exception to re throw as it is or throw a new exception? You will be doing more or less same even if you implement your own SQLExceptionConverter.
顺便说一句,为什么不能有一个全局过滤器,它可以捕获每个异常并决定按原样重新抛出哪个异常或抛出一个新异常?即使您实现自己的 SQLExceptionConverter,您也会或多或少地做同样的事情。
回答by THIHA SOE
according to my experience, you should catch the SQLException, and then u can get easily the SQL error code for specific database. Eg: your database is mysql and u got error code 1062 . So you can know that error is Duplicated entry error. You can check the mysql error code http://www.briandunning.com/error-codes/?source=MySQL
根据我的经验,您应该捕获SQLException,然后您可以轻松获取特定数据库的SQL错误代码。例如:您的数据库是 mysql 并且您收到错误代码 1062 。所以你可以知道错误是重复输入错误。可以查看mysql错误代码 http://www.briandunning.com/error-codes/?source=MySQL