java - 为什么@Valid会导致可重入刷新?

标签 java jpa openjpa hibernate-validator

当使用具有 @OneToMany 关系的 @Valid 注释时,在尝试更新实体时出现 org.apache.openjpa.persistence.InvalidStateException: Detected reentrantlush. 异常。如果没有 @Valid 注释,更新工作正常。

为什么@Valid注释会导致这里的异常?

@Entity
@Table(name = "code")
public class Code {

    @Valid // <-- THE PROBLEM
    @OneToMany(mappedBy = "code", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private List<File> files;

    // ...
}

@Entity
@Table(name = "file")
public class File {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Min(value = 0)
    @Column(name = "id")
    private long id;

    @ManyToOne
    @JoinColumn(name = "code_id")
    private Code code;

    // ...

    @Override
    public int hashCode() {
        return (int) id;
    }
}

异常发生在:

entityManager.getTransaction().commit();

完整的异常:

<openjpa-2.2.2-r422266:1468616 fatal user error> org.apache.openjpa.persistence.InvalidStateException: Detected reentrant flush.  Make sure your flush-time instance callback methods or event listeners do not invoke any operations that require the in-progress flush to complete.
    at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2078)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1853)
    at org.apache.openjpa.kernel.StateManagerImpl.assignObjectId(StateManagerImpl.java:596)
    at org.apache.openjpa.kernel.StateManagerImpl.assignField(StateManagerImpl.java:683)
    at org.apache.openjpa.kernel.StateManagerImpl.beforeAccessField(StateManagerImpl.java:1655)
    at org.apache.openjpa.kernel.StateManagerImpl.accessingField(StateManagerImpl.java:1586)
    at entities.File.pcGetid(File.java)
    at entities.File.hashCode(File.java:144)
    at org.hibernate.validator.internal.engine.ValidationContext$BeanAndPath.hashCode(ValidationContext.java:610)
    at java.util.HashMap.hash(HashMap.java:366)
    at java.util.HashMap.getEntry(HashMap.java:466)
    at java.util.HashMap.get(HashMap.java:421)
    at org.hibernate.validator.internal.engine.ValidationContext.hasMetaConstraintBeenProcessed(ValidationContext.java:336)
    at org.hibernate.validator.internal.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:1281)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:475)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:424)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:388)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:340)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateCascadedConstraint(ValidatorImpl.java:635)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateCascadedConstraints(ValidatorImpl.java:524)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:349)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:158)
    at org.apache.openjpa.lib.util.J2DoPrivHelper$61.run(J2DoPrivHelper.java:1254)
    at org.apache.openjpa.lib.util.J2DoPrivHelper$61.run(J2DoPrivHelper.java:1252)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.openjpa.persistence.validation.ValidatorImpl.validate(ValidatorImpl.java:278)
    at org.apache.openjpa.validation.ValidatingLifecycleEventManager.fireEvent(ValidatingLifecycleEventManager.java:123)
    at org.apache.openjpa.kernel.BrokerImpl.fireLifecycleEvent(BrokerImpl.java:810)
    at org.apache.openjpa.kernel.StateManagerImpl.fireLifecycleEvent(StateManagerImpl.java:419)
    at org.apache.openjpa.kernel.StateManagerImpl.preFlush(StateManagerImpl.java:3007)
    at org.apache.openjpa.kernel.PDirtyState.beforeFlush(PDirtyState.java:39)
    at org.apache.openjpa.kernel.StateManagerImpl.beforeFlush(StateManagerImpl.java:1034)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2122)
    at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2082)
    at org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java:2000)
    at org.apache.openjpa.kernel.LocalManagedRuntime.commit(LocalManagedRuntime.java:81)
    at org.apache.openjpa.kernel.BrokerImpl.commit(BrokerImpl.java:1524)
    at org.apache.openjpa.kernel.DelegatingBroker.commit(DelegatingBroker.java:933)
    at org.apache.openjpa.persistence.EntityManagerImpl.commit(EntityManagerImpl.java:570)
    at entities.CodePersistenceTest.updateCode(CodePersistenceTest.java:768)

我正在使用 OpenJPA 2.2.2 和 Hibernate Validator 5.0.2

最佳答案

您引用的是File#hashCode()中的对象id,并使用生成类型IDENTITY,它将从数据库中的标识列获取id值,即需要访问数据库才能获取id。显然,您的实体的检测方式会导致在读取 id 属性时为此目的发生刷新。

您可以尝试更改您的 hashCode() 实现,使其不基于主键,而是基于实体的“业务身份”,例如文件名或路径。

也就是说,我认为 Hibernate Validator 在这种情况下不应该调用实体的 hashCode() 方法,因此我提交了 HV-848改变这一点。

关于java - 为什么@Valid会导致可重入刷新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20789369/

相关文章:

java - 使用 Visual Studio Code Portable 下载 Java 扩展时出现问题

mysql - 使用参数值作为参数名称的 SQL 注入(inject)

java - 如何与 JPA 正确处理 FK 关系

java - 重新加载持久化实体

java - 为什么Java Set包含一个要添加的对象,但没有Set元素等于该对象

java - 除非有对 Thread.sleep(...) 的调用,否则简单的 spring 应用程序将终止

java - Java中的协作工具-在客户端之间共享数据的最佳(最简单)体系结构?

Java Springs 连接池 com.mchange.v2.c3p0.impl.NewProxyConnection

hibernate - 为什么 Hibernate 的 EntityManager.find() impl 会抛出 EntityNotFoundException?

java - 有没有一种简单的方法将 varchar2(1) ("1"或 "0") 映射到 JPA boolean 类型