Java 休眠异常处理
我有一个“复杂”的问题。Java 休眠异常处理,java,hibernate,exception-handling,Java,Hibernate,Exception Handling,我有一个“复杂”的问题。 我正在使用Hibernate/JPA与DB进行事务处理。 我不是DBA,客户机使用我的应用程序,一个RESTful web服务。我的问题是DB被改变了(不是很频繁,但它仍然在改变)。此外,客户端并不总是尊重我的应用程序的输入(长度、类型等)。当这种情况发生时,Hibernate抛出一个异常。异常很难从日志中翻译和读取,因为它有嵌套的异常,并且包含大量文本:正如我所说,非常难以理解。 我想知道是否有可能在实体级别处理异常,抛出一个定制的异常 我提前感谢你的耐心和帮助 编辑
我正在使用Hibernate/JPA与DB进行事务处理。
我不是DBA,客户机使用我的应用程序,一个RESTful web服务。我的问题是DB被改变了(不是很频繁,但它仍然在改变)。此外,客户端并不总是尊重我的应用程序的输入(长度、类型等)。当这种情况发生时,Hibernate抛出一个异常。异常很难从日志中翻译和读取,因为它有嵌套的异常,并且包含大量文本:正如我所说,非常难以理解。
我想知道是否有可能在实体级别处理异常,抛出一个定制的异常 我提前感谢你的耐心和帮助 编辑: 最后我成功地做到了我想做的,不确定是否做对了 App.java HibernateUtil.java Oracle数据库结构
我不确定你所说的“实体级”是什么意思,但可以肯定。在调用Hibernate的代码周围放置一个try/catch。抓住你想扔的东西,然后再扔。诀窍是在抛出的异常中加入一些有意义的理由
当然,最重要的一点是,您应该验证所有输入。您可以实现自己的输入,并以您想要的方式进行处理 使用属性“hibernate.jdbc.sql\u exception\u converter”设置自定义转换器 我找不到更多的文档,您需要深入研究Hibernate的实现来找到更多
顺便问一下,为什么不能有一个全局过滤器,它捕获每个异常并决定重新抛出哪个异常或抛出一个新异常?即使您实现了自己的SQLExceptionConverter,您也将或多或少地执行相同的操作。我倾向于在获得DB级别之前进行同样多的验证。看看Hibernate验证器,它是JSR-303的参考实现 使用标准注释,您可以在尝试将实体放入数据库之前实施约束并获得良好的错误消息
我相信这将允许您按照要求在实体级别进行验证。根据我的经验,您应该捕获SQLException,然后您可以轻松获得特定数据库的SQL错误代码。 你的数据库是mysql,你得到了错误代码1062。所以您可以知道该错误是重复输入错误。您可以检查mysql错误代码
我希望在异常中有一些细节(不要太多),而不进行过度的输入验证。比如说,如果您的数据库对字符串长度等有限制,并且输入不符合要求,hibernate尝试插入数据会抛出ConstraintViolationException,您可以捕获并重新抛出它,说是非法参数;在这种情况下,还要记录输入。嗯,同意这一点。在大多数情况下,基于Hibernate/DB exception处理业务异常为时已晚。我尝试了这个方法,似乎已经足够好了,但仍然会遇到这样的长异常。我只是想抛出“插入信息”你有没有读过这个问题?他使用JPA,Oracle作为底层RDBMS。
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();
}
}
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();
}
}
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;
}
}
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 + " ]";
}
}
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();
}
}
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;