在同一规划实体中使用@PlanningListVariable与@PlanningVariable报错求助
问题场景
在课程规划实体LessonDto中定义了两个@PlanningVariable(room、timeslot)和一个@PlanningListVariable(studentGroups),添加列表变量前程序运行正常,添加后触发如下报错:
Caused by: java.lang.IllegalArgumentException: The config (ValueSelectorConfig(null)) has no configured variableName for entityClass (class com.tmtblback.dto.LessonDto) and because there are multiple variableNames ([room, studentGroups, timeslot]), it cannot be deduced automatically.
原因分析
当实体同时存在多个规划变量(包括常规@PlanningVariable和集合类型的@PlanningListVariable)时,OptaPlanner无法自动判断值选择器(ValueSelector)对应的目标变量,必须显式指定变量名称。
解决方法
1. XML配置中显式指定variableName
如果使用XML配置Solver,在移动选择器或值选择器的配置块中添加variableName属性,明确绑定目标变量:
<!-- 针对studentGroups的列表变更移动选择器 --> <moveSelector> <listChangeMoveSelector> <valueSelector variableName="studentGroups"/> </listChangeMoveSelector> </moveSelector> <!-- 针对room的普通变更移动选择器 --> <moveSelector> <changeMoveSelector> <valueSelector variableName="room"/> </changeMoveSelector> </moveSelector> <!-- 针对timeslot的普通变更移动选择器 --> <moveSelector> <changeMoveSelector> <valueSelector variableName="timeslot"/> </changeMoveSelector> </moveSelector>
2. Java注解配置中指定变量名
如果采用Java注解配置Solver,在Phase配置中为每个移动选择器明确指定variableName:
@PlanningEntity public class LessonDto { // 常规规划变量 @PlanningVariable(valueRangeProviderRefs = "roomRange") private Room room; @PlanningVariable(valueRangeProviderRefs = "timeslotRange") private Timeslot timeslot; // 集合类型规划变量 @PlanningListVariable(valueRangeProviderRefs = "studentGroupRange") private List<StudentGroup> studentGroups; // getter/setter省略 } // Solver配置示例 @Configuration public class SolverConfig { @Bean public Solver<LessonSolution> lessonSolver() { return SolverFactory.create(new SolverConfig() .withSolutionClass(LessonSolution.class) .withEntityClasses(LessonDto.class) .withPhaseList(List.of( // 构造启发式阶段 new ConstructionHeuristicPhaseConfig(), // 局部搜索阶段,为每个变量配置对应的移动选择器 new LocalSearchPhaseConfig() .withMoveSelectorConfigList(List.of( new ListChangeMoveSelectorConfig() .withVariableName("studentGroups"), new ChangeMoveSelectorConfig() .withVariableName("room"), new ChangeMoveSelectorConfig() .withVariableName("timeslot") )) )) ).buildSolver(); } }
3. 统一检查所有移动选择器配置
确保所有涉及值选择的移动选择器(如ChangeMoveSelector、ListChangeMoveSelector、SwapMoveSelector等)都明确绑定了variableName,避免OptaPlanner尝试自动推导变量。
内容的提问来源于stack exchange,提问作者Nikola Bakic

