When using a @Valid annotation with @OneToMany relationship, I get a org.apache.openjpa.persistence.InvalidStateException: Detected reentrant flush. exception when trying to update my entity. Without the @Valid annotation, the update works fine.

Why is the @Valid annotation causing the exception here?

@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;
    }
}

The exception happens on:

entityManager.getTransaction().commit();

The full exception:

<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)

I'm using OpenJPA 2.2.2 with Hibernate Validator 5.0.2

有帮助吗?

解决方案

You're referring to the object id in File#hashCode() and are using generation type IDENTITY which will obtain id values from an identity column in the database, i.e. an access to the DB is required to obtain ids. Apparently your entities are instrumented in a way which causes a flush to happen for that purpose just when reading the id property.

You could try and change your hashCode() implementation in a way that it isn't based on the primary key but on the "business identity" of the entity, e.g. the file name or path.

That said, I think Hibernate Validator shouldn't invoke the hashCode() method of entities in this situation, so I've filed HV-848 to get this changed.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top