我有一个“复杂”的问题。
我正在使用Hibernate / JPA与数据库进行事务。
我不是DBA,客户端使用了我的应用程序,即RESTful Web服务。我的问题是数据库已更改(不是很频繁,但仍在更改)。另外,客户端并不总是尊重我的应用程序的输入(长度,类型等)。发生这种情况时,Hibernate会引发异常。异常很难翻译和从日志中读取,因为它具有嵌套的异常并且由很多文本组成:就像我说的那样,很难理解。
我想知道是否有可能在实体级别处理异常,并抛出定制的异常。
感谢您的耐心配合和事先的帮助。
编辑:
最后,我设法做自己想做的事,不确定这样做是否 正确 。
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; } }
股票.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;
在获得数据库级别之前,我倾向于做很多验证。看看Hibernate Validator,http: //www.hibernate.org/subprojects/validator.html,它是JSR-303的参考实现。
使用标准注释,您可以强制约束并获得良好的错误消息,然后再尝试将实体放入数据库中。
我相信这将使您能够按要求在实体级别进行验证。