2011-10-10 160 views
2

我有一個'複雜'的問題。
休眠異常處理

我正在使用Hibernate/JPA與數據庫進行交易。

我不是DBA,客戶端使用我的應用程序,一個RESTful Web服務。我的問題是數據庫被改變(不是很頻繁,但仍然會改變)。此外,客戶並不總是尊重我的應用程序(長度,類型等)的輸入。發生這種情況時,Hibernate會拋出異常。這個異常很難翻譯和從日誌中讀取,因爲它嵌套了異常,並且包含很多文本:就像我說的,很難理解。

我想知道是否有可能在實體級別上處理異常,拋出一個定製的異常。

我感謝您的耐心和提前幫助。

編輯:

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<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數據庫結構

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; 

回答

2

我傾向於你的數據庫級前做盡可能多的驗證。看看Hibernate Validator,http://www.hibernate.org/subprojects/validator.html這是JSR-303的參考實現。

使用標準註釋可以強制約束,取得了很好的錯誤信息嘗試把實體到數據庫之後。

我相信這將允許按要求你在實體層面驗證。

+0

同意這一點。在大多數情況下,處理基於Hibernate/DB異常的業務異常爲時已晚。 –

+0

我試過這個,看起來不錯,但我仍然得到像http://pastebin.com/RT2JdQca這樣的很長的例外。我只是想拋出「interpolatedMessage」 –

0

我不知道你的意思 「實體級別」 是什麼,但當然。圍繞調用Hibernate的代碼放一個try/catch。捕捉拋出並重新拋出任何你想要的。訣竅是提出一些理由,在你拋出的例外中是有道理的。

當然,重要的一點是您應該驗證所有輸入。

+0

我想在例外的一些細節(不要太多),而不做一個excesive輸入驗證 –

+0

所以說,如果你的數據庫有關於串等,所有的長度約束上,輸入沒有按」 t符合它,hibernate嘗試插入數據會拋出一個ConstraintViolationException,你可以捕獲並重新拋出它說IllegalArguments;在這種情況下也要記錄輸入。 HTH。 – Scorpion

0

您可以實施自己的SQLExceptionConverter並按照自己想要的方式進行處理。

使用屬性'hibernate.jdbc.sql_exception_converter'來設置您的自定義轉換器。

我無法找到更多的文檔,你需要挖掘到Hibernate的實現來找到更多。

順便說一句,你爲什麼不能有一個全球性的過濾器,捕捉每一個異常,並決定哪些例外,因爲它是重新拋出或拋出一個新的異常?即使你實現了你自己的SQLExceptionConverter,你也會做的差不多。

0

根據我的經驗,你應該抓住的SQLException,然後ü可以很容易地獲得特定數據庫的SQL錯誤代碼。例如:你的數據庫是mysql,你的錯誤代碼是1062。所以你可以知道錯誤是重複輸入錯誤。您可以查看MySQL錯誤代碼 http://www.briandunning.com/error-codes/?source=MySQL

+0

你有沒有打擾讀這個問題?他使用JPA,將Oracle作爲基礎RDBMS。 –

+0

這是我的錯,我編輯了添加代碼的問題 –