gpt4 book ai didi

hibernate - JPA ConstraintViolation 与回滚

转载 作者:行者123 更新时间:2023-12-04 22:33:35 30 4
gpt4 key购买 nike

我想我刚刚发现两种不同的 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 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 验证。

EclipseLink 在标记回滚的 EntityManager#persist() 上抛出 CVE。
并且 Hibernate 在 EntityTransaction#commit() 上抛出 RB 。

最佳答案

以下是有关您的行为的更详细来源。
根据 JPA 2 specification(第 102 页)

If the set of ConstraintViolation objects returned by the validate method is notempty, the persistence provider must throw the javax.validation.ConstraintViolationException containing a reference to the returned set of ConstraintViolation objects, andmust mark the transaction for rollback.


hibernate doc

If an entity is found to be invalid, the list of constraint violations is propagated by the ConstraintViolationException which exposes the set of ConstraintViolations.

This exception is wrapped in a RollbackException when the violationhappens at commit time. Otherwise the ConstraintViolationException isreturned [by Hibernate Validator] (for example when calling flush().)


此外,来自 jpa 2 规范(第 101 页)

By default, the default Bean Validation group (the group Default) will be validated upon the pre-persist and pre-update lifecycle validation events


将所有这些放在一起,我有点困惑,因为在我看来 HibernatePersistenceProvider 的行为不遵循 JPA 2 规范,因为:
  • 验证必须在“pre-presist”
  • 上执行
  • 持久性提供程序 必须抛出 ConstraintViolationException

  • 显然,在您的情况下,在调用 ConstraintViolationException 时(以及使用 HibernatePersistenceProvider 时)不会抛出 persist
    所以根据我的理解并回答你的问题:
  • eclipselink 是对的
  • hibernate 是错误的

  • (注:希望别人能证实或不同意我的分析)

    重要编辑
    我对自己的结论感到困惑。所以我试图重现 OP 描述的行为,但我无法立即重现这种行为。
    我所做的与 OP 所描述的非常相似:
  • 设置了一个小项目,其中一个实体带有 @NotNull 字段。
  • 尝试在一个简单的测试中为 @NotNull 字段持久化()一个为 null 的实例。
  • 断言 persist() 操作抛出 javax.validation.ConstraintViolationException + 将交易标记为 rollback only
  • 在使用 eclipselink 作为持久性提供者时这样做 --> 成功的
  • 在使用 hibernate 作为持久性提供者时这样做 --> 成功的

  • 我的测试和描述 OP 的测试之间的主要区别是 id 生成。
    在我成功的测试中,我使用了一个简单的 @GeneratedValue
    将 id 生成策略更改为:
    @GeneratedValue(strategy = GenerationType.TABLE,
    generator = "NAME_MUST_NOT_BE_NULL_ID_GENERATOR")
    @TableGenerator(name = "NAME_MUST_NOT_BE_NULL_ID_GENERATOR",
    pkColumnValue = "NAME_MUST_NOT_BE_NULL_ID")
    我发现了 OP 描述的确切行为:
  • 使用 eclipselink 时由 javax.validation.ConstraintViolationException 抛出的 persist()
  • 使用 hibernate 时 persist() 根本没有抛出异常。

  • 所以,当使用 Hibernate + strategy = GenerationType.TABLE 时:行为是不同的。我很确定它没有遵循 JPA2 规范。

    关于hibernate - JPA ConstraintViolation 与回滚,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14354849/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com