问题
Problem
Im using hibernate 5.4.11 core and when i try to update large amounts of entities i run into that issue right here. It looks like an hibernate internal exception.
Update
I tested it with hibernate 5.4.23, the same exception occured. I also updated my "mysql connector" to the latest version "8.0.22" and it didnt worked either, the same exception.Describtion
This is how i update my entities, it runs in another thread, which should not be the problem here... its basically a own thread for database operations, during that operation no other database operations are running.
// 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);
The class i try to save
@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; }
}
The exception i get
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)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Anyone having an idea why hibernate throws that internal exception and what i could do to solve this issue ?
回答1:
You shouldn't change the list of entities
, identities
or components
that you persist/update while another thread is working on it.
The thread that persists/updates entities must own the entity objects i.e. no other thread may interfere with the state while the transaction is running. You can only make use of the objects again after the transaction finished.
I don't know what your application does, but if you need access to the objects in your main thread you could copy the objects for the flush operation. This way, the thread doing the flush will properly own the objects and can safely work on them.
来源:https://stackoverflow.com/questions/64719951/concurrentmodificationexception-when-updating-my-entities