Problème

J'utilise le noyau hibernate 5.4.11 et lorsque j'essaie de mettre à jour de grandes quantités d'entités, je rencontre ce problème ici. Cela ressemble à une exception interne d'hibernation.

Mise à jour



La description

C'est ainsi que je mets à jour mes entités, elles s'exécutent dans un autre thread, ce qui ne devrait pas être le problème ici...

        // Run the database operation for updating the entities async in a new thread, return updated entities once done
        return CompletableFuture.runAsync(() -> {

            var session = database.openSession();
            session.beginTransaction();

            try {

                // Save identities first
                for (var identity : identities)
                    session.update(identity);

                // Update components, each component-type
                for(var insertedClass : insertationOrder)
                    for (var hbc : componentsPerType.get(insertedClass))
                        session.update(hbc);

                session.flush();
                session.clear();

                session.getTransaction().commit();
            } catch (Exception e){

                var messageComposer = new ExceptionMessageComposer(e);
                GameExtension.getInstance().trace("Update : "+messageComposer.toString());
                session.getTransaction().rollback();
            }

            session.close();
        }).thenApply(v -> entities);

La classe que j'essaye de sauver

@Entity
@Table(name = "chunk", uniqueConstraints = {@UniqueConstraint(columnNames={"x", "y"})}, indexes = {@Index(columnList = "x,y")})
@Access(value = AccessType.FIELD)
@SelectBeforeUpdate(false)
public class Chunk extends HibernateComponent{

    public int x;
    public int y;
    public Date createdOn;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "chunk_identity", joinColumns = @JoinColumn(name = "identity_id"), inverseJoinColumns = @JoinColumn(name = "id"), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
    @Fetch(FetchMode.JOIN)
    @BatchSize(size = 50)
    public Set<Identity> inChunk = new LinkedHashSet<>();

    @Transient
    public Set<ChunkLoader> loadedBy = new LinkedHashSet<>();

    public Chunk() {}
    public Chunk(int x, int y, Date createdOn) {
        this.x = x;
        this.y = y;
        this.createdOn = createdOn;
    }
}



@Entity
@Table(name = "location")
@Access(AccessType.FIELD)
public class Transform extends HibernateComponent {

    @Embedded
    public Vector2 position = new Vector2();

    @ManyToOne
    @JoinColumn(name = "chunk_id", referencedColumnName = "identity_id", updatable = false)
    @Fetch(FetchMode.JOIN)
    public Chunk chunk;

    @Transient
    public Vector2 previousPos = new Vector2();

    public Transform() {}
    public Transform(Vector2 position) { this.position = position; }
    public Transform(Vector2 position, Chunk chunk) { this.position = position; this.chunk = chunk; }
}

L'exception que j'obtiens

19:29:05,126 INFO  [ForkJoinPool.commonPool-worker-19] Extensions     - {Game}: Update : java.util.ConcurrentModificationException:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Exception: java.util.ConcurrentModificationException
Message: *** Null ***
+--- --- ---+
Stack Trace:
+--- --- ---+
java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1493)
java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1516)
org.hibernate.persister.collection.AbstractCollectionPersister.insertRows(AbstractCollectionPersister.java:1555)
org.hibernate.action.internal.CollectionUpdateAction.execute(CollectionUpdateAction.java:89)
org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478)
java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:475)
org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:348)
org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:40)
org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102)
org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1356)
org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1343)
com.parallelorigin.persistence.database.ECSDatabase.lambda$updateEntities$16(ECSDatabase.java:242)
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Quelqu'un a-t-il une idée de la raison pour laquelle l'hibernation lève cette exception interne et ce que je pourrais faire pour résoudre ce problème ?

0
genaray 6 nov. 2020 à 21:35

1 réponse

Meilleure réponse

Vous ne devez pas modifier la liste des entities, identities ou components que vous persistez/mettez à jour pendant qu'un autre thread y travaille.

Le thread qui persiste/met à jour les entités doit posséder les objets d'entité, c'est-à-dire qu'aucun autre thread ne peut interférer avec l'état pendant l'exécution de la transaction. Vous ne pouvez réutiliser les objets qu'une fois la transaction terminée.

Je ne sais pas ce que fait votre application, mais si vous avez besoin d'accéder aux objets de votre thread principal, vous pouvez copier les objets pour l'opération de vidage. De cette façon, le thread effectuant le flush possédera correctement les objets et pourra travailler sur eux en toute sécurité.

3
Christian Beikov 7 nov. 2020 à 10:43