克隆实体上的 java.util.ConcurrentModificationException

标签 java groovy jspresso

我编写了一个自定义策略,以便克隆一个实体 TrainTimetable 及其槽位 集合。

下面是模型的摘录:

Entity('TrainTimetable') {
    list        'slots'             , ref:'Slot', composition:true
}
Entity('Slot') {
    reference   'trainTimetable'    , ref:'TrainTimetable', reverse:'TrainTimetable-slots'
}

方法如下:

package fr.yc.rail.backend;

import java.util.ArrayList;
import java.util.List;

import org.jspresso.framework.model.entity.IEntity;
import org.jspresso.framework.model.entity.IEntityFactory;
import org.jspresso.framework.model.entity.SmartEntityCloneFactory;

import fr.yc.rail.model.Slot;
import fr.yc.rail.model.TrainTimetable;

public class CloneTrainTimetableFactory extends SmartEntityCloneFactory {

    @Override
    public <E extends IEntity> E cloneEntity(E entityToClone,
            IEntityFactory entityFactory) {

        TrainTimetable clonedTrainTimetable = (TrainTimetable) super.cloneEntity(entityToClone, entityFactory);

        TrainTimetable trainTimetableToClone = (TrainTimetable) entityToClone;

        List<Slot> clonedSlots = new ArrayList<Slot>();

        trainTimetableToClone.getSlots().each {
            Slot clonedSlot = super.cloneEntity(it, entityFactory);
            clonedSlots.add(clonedSlot);
        }

        clonedTrainTimetable.setSlots(clonedSlots);

        return (E) clonedTrainTimetable;
    }
}

调用该方法时,下面的代码在第二次迭代时引发 ConcurrentModificationException 错误:

trainTimetableToClone.getSlots().each {
    Slot clonedSlot = super.cloneEntity(it, entityFactory);
    clonedSlots.add(clonedSlot);
}

在堆栈跟踪下方:

ERROR <2015-06-09 18:13:11,193> org.jspresso.framework.application.frontend.controller.AbstractFrontendController : An unexpected error occurred for user demo on session 4379dc1b.
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at org.hibernate.collection.internal.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:774)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1378)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1372)
    at org.codehaus.groovy.runtime.dgm$149.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at fr.yc.rail.backend.CloneTrainTimetableFactory.cloneEntity(CloneTrainTimetableFactory.groovy:25)
    at org.jspresso.framework.application.backend.action.CloneComponentCollectionAction.cloneElement(CloneComponentCollectionAction.java:61)
    at org.jspresso.framework.application.backend.action.AbstractCloneCollectionAction.getAddedComponents(AbstractCloneCollectionAction.java:71)
    at org.jspresso.framework.application.backend.action.AbstractAddCollectionToMasterAction.execute(AbstractAddCollectionToMasterAction.java:78)
    at org.jspresso.framework.application.backend.AbstractBackendController.execute(AbstractBackendController.java:403)
    at org.jspresso.framework.application.frontend.controller.AbstractFrontendController.executeBackend(AbstractFrontendController.java:1536)
    at org.jspresso.framework.application.frontend.controller.AbstractFrontendController.execute(AbstractFrontendController.java:576)
    at org.jspresso.framework.application.action.AbstractAction.execute(AbstractAction.java:114)
    at org.jspresso.framework.application.frontend.controller.AbstractFrontendController.executeFrontend(AbstractFrontendController.java:1549)
    at org.jspresso.framework.application.frontend.controller.AbstractFrontendController.execute(AbstractFrontendController.java:578)
    at org.jspresso.framework.view.remote.RemoteActionFactory$ActionAdapter.actionPerformed(RemoteActionFactory.java:235)
    at org.jspresso.framework.application.frontend.controller.remote.AbstractRemoteController.handleCommand(AbstractRemoteController.java:440)
    at org.jspresso.framework.application.frontend.controller.remote.AbstractRemoteController.handleCommands(AbstractRemoteController.java:202)
    at org.jspresso.framework.application.startup.remote.RemoteStartup.handleCommands(RemoteStartup.java:88)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at net.sf.qooxdoo.rpc.RemoteCallUtils.callCompatibleMethod(RemoteCallUtils.java:469)
    at net.sf.qooxdoo.rpc.RpcServlet.handleRPC(RpcServlet.java:374)
    at net.sf.qooxdoo.rpc.RpcServlet.doPost(RpcServlet.java:481)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.jspresso.framework.util.http.HttpRequestHolder.doFilter(HttpRequestHolder.java:99)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)

该错误意味着我正在迭代一个大小正在变化的集合,但我不知道如何摆脱它。

最佳答案

TrainTimetableSlot 之间存在 1-N 双向关联。这意味着每当您更新关联的一侧时,Jspresso 将负责更新另一侧以保持模型一致性,例如:

  • 如果您在某个时段设置 trainTimetable 属性,该时段将被添加到火车时刻表的反向 slots 集合中。
  • 如果您向列车时刻表的 slots 属性添加一个时段,则添加时段的 trainTimetable 反向属性将更新为列车时刻表。<

在以下代码中:

 Slot clonedSlot = super.cloneEntity(it, entityFactory);

由于前面解释的规则,clonedSlot 将被添加到 trainTimeTableToClone 槽中。所以迭代后的集合实际上发生了变化。

为了解决这个问题,在迭代之前制作集合的副本就足够了,即:

trainTimetableToClone.getSlots().collect().each {

关于克隆实体上的 java.util.ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30737812/

相关文章:

java - Jspresso:集合内容更改时未调用监听器

Java StackOverFlowError - 错误的递归调用?

java - 如何使用数据库存储的 GString 定义生成运行时数据

java - 复制具有双向关系的对象

grails - 如何在Grails中找出已连接的客户端IP地址

collections - 在 Groovy 列表中查找元素的所有索引

java - 有没有办法在接口(interface)上声明计算属性?

java - 在 Jspresso 中创建一个过滤器 View ,其中包含不带比较运算符的日期选择器

java - 在 Java 中确定没有 InputEvent 对象的修饰键状态

java - 为什么我的延迟没有发生?