与Bean验证API结合使用时,Hibernate不遵循JPA规范? [英] Hibernate not following JPA specifications when combined with Bean Validation API?
问题描述
这个问题跟随这个问题: JPA约束违规vs回滚
我对JPA和验证API(JSR-303)的组合进行了一些测试。
在 JPA规范(第101-102页):
lockquote
默认情况下,默认的Bean验证组(默认组)在预留持续时间和更新前的生命周期验证事件中进行验证。
...
由validate方法返回的ConstraintViolation对象不为空,则持久性提供程序必须抛出包含对返回的ConstraintViolation对象集的引用的javax.validation.ConstraintViolationException,并且必须标记tr
我设置了以下测试: 问题是: 以下是我测试的代码: 实体 默认策略
NameNotNullWithDefaultGeneratedStrategy $ (code> @Generated
)和 @NotNull字符串名称
列
NameNotNullWithTableGeneratedStrategy
,其中包含使用表策略( @TableGenerated
)和 @NotNull字符串名称
列
名称
。
javax.validation.ConstraintViolationException
由persist方法抛出,标记为仅回滚
的事务这些假设是基于本文中引用的JPA规范)。
$ b 结果如下:
persist
方法为这两个实体抛出 javax.validation.ConstraintViolationException
。
rollback只有
在这两种情况下
persist
为实体抛出一个
+标记为 javax.validation.ConstraintViolationException
NameNotNullWithDefaultGeneratedStrategy 仅回滚的交易$ c>
persist
不要为实体 NameNotNullWithTableGeneratedStrategy
+事务未标记为仅回滚
>
commit
用于 NameNotNullWithTableGeneratedStrategy
失败,并且 RollbackException
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(){//成功
testPersistWithNullName(pu-hibernate,new NameNotNullWithDefaultGeneratedStrategy( ));
}
@Test
public void testHibernateTableStrategy(){
testPersistWithNullName(pu-hibernate,new NameNotNullWithTableGeneratedStrategy());
//此测试失败:
//java.lang.AssertionError:期望javax.validation.ConstraintViolationException,但persist()成功!
$ b @Test
public void testEclipseLinkDefaultStrategy(){//成功
testPersistWithNullName(pu-eclipselink,new NameNotNullWithDefaultGeneratedStrategy());
$ b @Test
public void testEclipseLinkTableStrategy(){//成功
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();
尝试{
尝试{
entityManager.persist(entity);
Assert.fail(期待javax.validation.ConstraintViolationException,但persist()成功!);
} catch(javax.validation.ConstraintViolationException cve){
//预计
Assert.assertTrue(根据JPA规范事务必须被标记为只回滚,transaction.getRollbackOnly()) ;
catch(Exception e){
Assert.assertTrue(根据JPA规范事务必须标记为只回滚,transaction.getRollbackOnly());
e.printStackTrace();
Assert.fail(期望得到一个javax.validation.ConstraintViolationException,但得到了+ e.getClass());
}
transaction.commit();
Assert.fail(持续使用null name !!!);
} catch(RollbackException e){
//这就是所期望的
} catch(Exception e){
e.printStackTrace();
Assert.fail(Unexpected exception:+ e.getMessage());
}
} finally {
entityManager.close();
$ b $
package com.example.jpa 。验证器;
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私人长ID;
@NotNull public String name;
public NameNotNullWithTableGeneratedStrategy(){}
}
持久性。 xml
<?xml version =1.0encoding =UTF-8?>
< persistence version =2.0xmlns =http://java.sun.com/xml/ns/persistence
xmlns:xsi =http://www.w3.org/ 2001 / XMLSchema-instance
xsi:schemaLocation =http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd >
< persistence-unit name =pu-hibernatetransaction-type =RESOURCE_LOCAL>
< provider> org.hibernate.ejb.HibernatePersistence< / provider>
< class> com.example.jpa.validator.NameNotNullWithTableGeneratedStrategy< / class>
< class> com.example.jpa.validator.NameNotNullWithDefaultGeneratedStrategy< / class>
<属性>
< property name =javax.persistence.jdbc.drivervalue =org.h2.Driver/>
< property name =javax.persistence.jdbc.urlvalue =jdbc:h2:mem:test_mem_hibernate/>
< property name =hibernate.hbm2ddl.autovalue =create-drop/>
< property name =hibernate.dialectvalue =org.hibernate.dialect.H2Dialect/>
< / properties>
< / persistence-unit>
< persistence-unit name =pu-eclipselinktransaction-type =RESOURCE_LOCAL>
< provider> org.eclipse.persistence.jpa.PersistenceProvider< / provider>
< class> com.example.jpa.validator.NameNotNullWithTableGeneratedStrategy< / class>
< class> com.example.jpa.validator.NameNotNullWithDefaultGeneratedStrategy< / class>
<属性>
< property name =javax.persistence.jdbc.drivervalue =org.h2.Driver/>
< property name =javax.persistence.jdbc.urlvalue =jdbc:h2:mem:test_mem/>
< property name =eclipselink.ddl-generationvalue =create-tables/>
< property name =eclipselink.target-databasevalue =org.eclipse.persistence.platform.database.H2Platform/>
< / properties>
< / persistence-unit>
< /余辉>
pom.xml
<?xml version =1.0encoding =UTF-8?>
< project xmlns =http://maven.apache.org/POM/4.0.0
xmlns:xsi =http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation =http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">
< modelVersion> 4.0.0< / modelVersion>
< groupId> com.example< / groupId>
< artifactId> com.example.jpa.validator< / artifactId>
< version> 1.0-SNAPSHOT< / version>
<属性>
< hibernate.version> 4.2.0.CR1< /hibernate.version>
< hibernate-validator.version> 4.3.1.Final< /hibernate-validator.version>
< junit.version> 4.11< /junit.version>
< h2.version> 1.3.170< /h2.version>
< / properties>
<依赖关系>
< dependency>
< groupId> org.hibernate< / groupId>
< artifactId> hibernate-validator< / artifactId>
< version> $ {hibernate-validator.version}< / version>
< /依赖关系>
< dependency>
< groupId> com.h2database< / groupId>
< artifactId> h2< / artifactId>
< version> $ {h2.version}< / version>
< scope> test< / scope>
< /依赖关系>
< dependency>
< groupId> junit< / groupId>
< artifactId> junit< / artifactId>
< scope> test< / scope>
< version> $ {junit.version}< / version>
< /依赖关系>
< dependency>
< groupId> org.hibernate< / groupId>
< artifactId> hibernate-core< / artifactId>
< version> $ {hibernate.version}< / version>
< /依赖关系>
< dependency>
< groupId> org.hibernate< / groupId>
< artifactId> hibernate-entitymanager< / artifactId>
< version> $ {hibernate.version}< / version>
< /依赖关系>
< dependency>
< groupId> org.eclipse.persistence< / groupId>
< artifactId> org.eclipse.persistence.jpa< / artifactId>
< version> 2.4.0< / version>
< /依赖关系>
< dependency>
< groupId> org.eclipse.persistence< / groupId>
< artifactId> javax.persistence< / artifactId>
< version> 2.0.0< / version>
< /依赖关系>
< /依赖关系>
< repositories>
< repository>
< url> http://download.eclipse.org/rt/eclipselink/maven.repo/< / url>
< id> eclipselink< / id>
< layout>预设< / layout>
< name>库EclipseLink库(JPA 2.0)< / name>
< / repository>
< / repositories>
< / project>
=https://hibernate.onjira.com/browse/HHH-8028 =nofollow> https://hibernate.onjira.com/browse/HHH-8028
This question is a follow up to this one : JPA ConstraintViolation vs Rollback
I did some test about combination of JPA and validation API (JSR-303).
I found the following in JPA specifications (page 101-102):
By default, the default Bean Validation group (the group Default) will be validated upon the pre-persist and pre-update lifecycle validation events
...
If the set of ConstraintViolation objects returned by the validate method is not empty, the persistence provider must throw the javax.validation.ConstraintViolationException containing a reference to the returned set of ConstraintViolation objects, and must mark the transaction for rollback.
I setup the following test:
- HibernateValidator as JSR-303 implementation
- 2 PersistenceProvider Hibernate and EclipseLink
- one entity
NameNotNullWithDefaultGeneratedStrategy
with an id generated with default strategy (@Generated
) and@NotNull String name
column - another entity
NameNotNullWithTableGeneratedStrategy
with an id generated with table strategy (@TableGenerated
) and@NotNull String name
column - the test try to
persist
one instance of each entity with a nullname
. - the expected results are a
javax.validation.ConstraintViolationException
thrown by the persist method and the transaction marked asrollback only
(i.e. those assumptions are based on JPA specification quoted in this post).
The results are :
- with eclipse link as a provider :
- the
persist
method throws ajavax.validation.ConstraintViolationException
for both entities. - the transaction is marked as
rollback only
in both cases
- the
- with hibernate as a provider :
persist
throws ajavax.validation.ConstraintViolationException
for entityNameNotNullWithDefaultGeneratedStrategy
+ transaction marked asrollback only
persist
don't throw any exception for entityNameNotNullWithTableGeneratedStrategy
+ transaction not flagged asrollback only
commit
forNameNotNullWithTableGeneratedStrategy
fails with aRollbackException
The questions are :
- is it really a violation of JPA specifications ? or I am missing something with particular case of table generated strategy ?
- in case it is a violation: is there any existing bug report related to it ?
Here is the code for my test:
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();
}
}
}
The entities
Default strategy
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() {}
}
Table stategy:
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() {}
}
The persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="pu-hibernate" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.example.jpa.validator.NameNotNullWithTableGeneratedStrategy</class>
<class>com.example.jpa.validator.NameNotNullWithDefaultGeneratedStrategy</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test_mem_hibernate"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
</properties>
</persistence-unit>
<persistence-unit name="pu-eclipselink" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.example.jpa.validator.NameNotNullWithTableGeneratedStrategy</class>
<class>com.example.jpa.validator.NameNotNullWithDefaultGeneratedStrategy</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test_mem"/>
<property name="eclipselink.ddl-generation" value="create-tables"/>
<property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.H2Platform"/>
</properties>
</persistence-unit>
</persistence>
The pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>com.example.jpa.validator</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<hibernate.version>4.2.0.CR1</hibernate.version>
<hibernate-validator.version>4.3.1.Final</hibernate-validator.version>
<junit.version>4.11</junit.version>
<h2.version>1.3.170</h2.version>
</properties>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
<repositories>
<repository>
<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
<id>eclipselink</id>
<layout>default</layout>
<name>Repository for library EclipseLink (JPA 2.0)</name>
</repository>
</repositories>
</project>
I filed a bug report for this : https://hibernate.onjira.com/browse/HHH-8028
这篇关于与Bean验证API结合使用时,Hibernate不遵循JPA规范?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!