简体   繁体   中英

Hibernate not following JPA specifications when combined with Bean Validation API?

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 null name .
  • the expected results are a javax.validation.ConstraintViolationException thrown by the persist method and the transaction marked as rollback only (ie those assumptions are based on JPA specification quoted in this post).

The results are :

  • with eclipse link as a provider :
    • the persist method throws a javax.validation.ConstraintViolationException for both entities.
    • the transaction is marked as rollback only in both cases
  • with hibernate as a provider :
    • persist throws a javax.validation.ConstraintViolationException for entity NameNotNullWithDefaultGeneratedStrategy + transaction marked as rollback only
    • persist don't throw any exception for entity NameNotNullWithTableGeneratedStrategy + transaction not flagged as rollback only
    • commit for NameNotNullWithTableGeneratedStrategy fails with a RollbackException

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>

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM