与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.ConstraintViolationException
和rollback 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
commit
因NameNotNullWithTableGeneratedStrategy
失败
-
问题是:
- 它真的违反了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
- Bean Validation Groupinheritance不适用于Group Sequence Provider
- Autowired Repository在Custom Constraint Validator中为空
- 如何让Spring MVC在JUnit测试中调用validation?
- 更新(合并)时缺少Spring Data JPAvalidation
- 如何获取ConstraintValidatorContext?
- JSR-303 Bean Validation注释多个字段
- JSF / Hibernate NotBlankvalidation
- JSF:JSR 303 Beanvalidation – 为什么在getter而不是setter?
- 方法上的Beanvalidation