我想我刚刚发现两种不同的 JPA 实现对于违反约束和回滚的工作方式不同。
@Test(expectedExceptions = @@.class) // CVE or RB?
public void testXXX() {
final EntityManager manager = LocalPU.createEntityManager();
try {
final EntityTransaction transaction = manager.getTransaction();
transaction.begin();
try {
manager.persist(<wrong>); // this is where CVE coming from
transaction.commit(); // this is where RB coming from
} catch (RollbackException re) {
// <---------------------------------------- hibernate here
throw re;
} catch (ConstraintViolationException cve) {
// <---------------------------------------- eclipselink here
transaction.rollback();
throw cve;
} catch (Exception e) {
transaction.rollback();
e.printStackTrace(System.err);
Assert.fail(e.getMessage());
}
} finally {
manager.close();
}
}
哪个实施工作正确?
更新
NameMustNotBeNull.java
@Entity
@Table(name = "NAME_MUST_NOT_BE_NULL")
public class NameMustNotBeNull {
protected NameMustNotBeNull() {
this(null);
}
public NameMustNotBeNull(final String name) {
super();
this.name = name;
}
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "NAME_MUST_NOT_BE_NULL_ID_GENERATOR")
@TableGenerator(name = "NAME_MUST_NOT_BE_NULL_ID_GENERATOR",
table = PrimaryKeyValue.TABLE,
pkColumnName = PrimaryKeyValue.PK_COLUMN_NAME,
valueColumnName = PrimaryKeyValue.VALUE_COLUMN_NAME,
pkColumnValue = "NAME_MUST_NOT_BE_NULL_ID")
@NotNull
@XmlTransient
private Long id;
@Basic(optional = false)
@Column(name = "NAME", nullable = false)
@NotNull
private String name;
}
NameMustNotBeNullTest.java
public class NameMustNotBeNullTest {
@Test(expectedExceptions = RollbackException.class)
public void testNullName() {
final EntityManager manager = LocalPU.createEntityManager();
try {
final EntityTransaction transaction = manager.getTransaction();
transaction.begin();
try {
final NameMustNotBeNull entity = new NameMustNotBeNull(null);
try {
manager.persist(entity);
} catch (ConstraintViolationException cve) {
System.out.println(cve.toString());
}
transaction.commit();
Assert.fail("persisted with null name");
} catch (RollbackException re) {
System.out.println(re.toString());
throw re;
} catch (Exception e) {
transaction.rollback();
e.printStackTrace(System.err);
Assert.fail(e.getMessage());
}
} finally {
manager.close();
}
}
}
持久性.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="localPU" transaction-type="RESOURCE_LOCAL">
<!-- I'm testing with one of following providers uncommented -->
<!--<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>-->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>....persistence.NameMustNotBeNull</class>
<properties>
<property name="javax.persistence.jdbc.driver"
value="org.apache.derby.jdbc.EmbeddedDriver"/>
<property name="javax.persistence.jdbc.url"
value="jdbc:derby:memory:corrsDB;create=true"/>
<!--<property name="javax.persistence.jdbc.user" value=""/>-->
<!--<property name="javax.persistence.jdbc.password" value=""/>-->
<!-- eclipselink -->
<property name="eclipselink.create-ddl-jdbc-file-name" value="target/createDDL.jdbc"/>
<property name="eclipselink.ddl-generation" value="create-tables"/>
<property name="eclipselink.ddl-generation.output-mode" value="both"/>
<property name="eclipselink.drop-ddl-jdbc-file-name" value="target/dropDDL.jdbc"/>
<property name="eclipselink.logging.level.sql" value="INFO"/>
<property name="eclipselink.logging.parameters" value="false"/>
<property name="eclipselink.target-database" value="Derby"/>
<!-- hibernate -->
<property name="hibernate.archive.autodetection" value="class" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect"/>
</properties>
</persistence-unit>
</persistence>
org.eclipse.persistence.jpa.PersistenceProvider
Running ...NameMustNotBeNullTest
1월 17, 2013 11:45:14 오전 org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 4.3.0.Final
javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details.
javax.persistence.RollbackException: Transaction rolled back because transaction was set to RollbackOnly.
org.hibernate.ejb.HibernatePersistence
Running ...NameMustNotBeNullTest
1월 17, 2013 11:50:14 오전 org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 4.3.0.Final
javax.persistence.RollbackException: Error while committing the transaction
如您所见,两个提供者似乎都启用了 Bean Validation。
EclipseLink 抛出 CVEEntityManager#persist()
并标记回滚。
并且 Hibernate 将 RB 抛出EntityTransaction#commit()
。