与Bean Validation API结合使用时,Hibernate不遵循JPA规范吗?

这个问题是对此问题的跟进: JPA ConstraintViolation vs Rollback

我做了一些关于JPA和validationAPI(JSR-303)组合的测试。

我在JPA规范中找到了以下内容(第101-102页):

默认情况下,默认Beanvalidation组(组默认值)将根据预先持久化和更新前的生命周期validation事件进行validation

如果validate方法返回的ConstraintViolation对象集不为空,则持久性提供程序必须抛出包含对返回的ConstraintViolation对象集的引用的javax.validation.ConstraintViolationException,并且必须将事务标记为回滚。

我设置了以下测试:

  • HibernateValidator作为JSR-303实现
  • 2 PersistenceProvider Hibernate和EclipseLink
  • 一个实体NameNotNullWithDefaultGeneratedStrategy ,其id使用默认策略( @Generated )和@NotNull String name列生成
  • 另一个实体NameNotNullWithTableGeneratedStrategy其中包含使用表策略( @TableGenerated )和@NotNull String name列生成的id
  • 测试尝试使用空name persist每个实体的一个实例。
  • 预期的结果是由persist方法抛出的javax.validation.ConstraintViolationExceptionrollback only标记为rollback only的事务(即这些假设基于本文中引用的JPA规范)。

结果是:

  • 使用eclipse链接作为提供者:
    • persist方法为两个实体抛出javax.validation.ConstraintViolationException
    • rollback only在两种情况下,事务都标记为rollback only
  • 以hibernate作为提供者:
    • persist抛出javax.validation.ConstraintViolationException用于实体NameNotNullWithDefaultGeneratedStrategy +事务标记为rollback only
    • NameNotNullWithTableGeneratedStrategy 不会为实体NameNotNullWithTableGeneratedStrategy +事务抛出任何exception而不会标记rollback only
    • 用于NameNotNullWithTableGeneratedStrategy commitNameNotNullWithTableGeneratedStrategy失败

问题是:

  • 它真的违反了JPA规范吗? 或者我错过了表格生成策略的特殊情况?
  • 如果是违规行为:是否有任何与之相关的错误报告?

这是我测试的代码:

 package com.example.jpa.validator; import org.junit.Assert; import org.junit.Test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import javax.persistence.RollbackException; public class ConstraintViolationExceptionTest { @Test public void testHibernateDefaultStrategy() { // Success testPersistWithNullName("pu-hibernate",new NameNotNullWithDefaultGeneratedStrategy()); } @Test public void testHibernateTableStrategy() { testPersistWithNullName("pu-hibernate",new NameNotNullWithTableGeneratedStrategy()); //this test fail with : //java.lang.AssertionError: Expecting a javax.validation.ConstraintViolationException, but persist() succeed ! } @Test public void testEclipseLinkDefaultStrategy() { // Success testPersistWithNullName("pu-eclipselink",new NameNotNullWithDefaultGeneratedStrategy()); } @Test public void testEclipseLinkTableStrategy() { // Success testPersistWithNullName("pu-eclipselink",new NameNotNullWithTableGeneratedStrategy()); } private void testPersistWithNullName(String persistenceUnitName, Object entity){ EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName); EntityManager entityManager = emf.createEntityManager(); try { final EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); try { try { entityManager.persist(entity); Assert.fail("Expecting a javax.validation.ConstraintViolationException, but persist() succeed !"); } catch (javax.validation.ConstraintViolationException cve) { //That's expected Assert.assertTrue("According JPA specs transaction must be flagged as rollback only",transaction.getRollbackOnly()); } catch (Exception e) { Assert.assertTrue("According JPA specs transaction must be flagged as rollback only",transaction.getRollbackOnly()); e.printStackTrace(); Assert.fail("Expecting a javax.validation.ConstraintViolationException, but got " + e.getClass()); } transaction.commit(); Assert.fail("persisted with null name !!!"); } catch (RollbackException e) { //That's expected } catch (Exception e) { e.printStackTrace(); Assert.fail("Unexpected exception :"+e.getMessage()); } } finally { entityManager.close(); } } } 

实体

默认策略

 package com.example.jpa.validator; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.validation.constraints.NotNull; @Entity public class NameNotNullWithDefaultGeneratedStrategy { @Id @GeneratedValue private Long id; @NotNull public String name; public NameNotNullWithDefaultGeneratedStrategy() {} } 

表状态:

 package com.example.jpa.validator; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.TableGenerator; import javax.validation.constraints.NotNull; @Entity public class NameNotNullWithTableGeneratedStrategy { @GeneratedValue(strategy = GenerationType.TABLE, generator = "NAME_MUST_NOT_BE_NULL_ID_GENERATOR") @TableGenerator(name = "NAME_MUST_NOT_BE_NULL_ID_GENERATOR") @Id @NotNull private Long id; @NotNull public String name; public NameNotNullWithTableGeneratedStrategy() {} } 

persistence.xml

     org.hibernate.ejb.HibernatePersistence com.example.jpa.validator.NameNotNullWithTableGeneratedStrategy com.example.jpa.validator.NameNotNullWithDefaultGeneratedStrategy         org.eclipse.persistence.jpa.PersistenceProvider com.example.jpa.validator.NameNotNullWithTableGeneratedStrategy com.example.jpa.validator.NameNotNullWithDefaultGeneratedStrategy         

pom.xml

    4.0.0 com.example com.example.jpa.validator 1.0-SNAPSHOT  4.2.0.CR1 4.3.1.Final 4.11 

1.3.170

org.hibernate hibernate-validator ${hibernate-validator.version} com.h2database h2 ${h2.version} test junit junit test ${junit.version} org.hibernate hibernate-core ${hibernate.version} org.hibernate hibernate-entitymanager ${hibernate.version} org.eclipse.persistence org.eclipse.persistence.jpa 2.4.0 org.eclipse.persistence javax.persistence 2.0.0 http://download.eclipse.org/rt/eclipselink/maven.repo/ eclipselink default Repository for library EclipseLink (JPA 2.0)

我提交了一份错误报告: https : //hibernate.onjira.com/browse/HHH-8028