OptaPlanner中Joiners无法正常工作问题求助
OptaPlanner中Joiners关联失效问题排查
我是OptaPlanner新手,正尝试修改员工排班示例。使用Joiners关联Shift与Availability类时,约束无法生效——员工仍被安排到其不可用的班次;但改用Filter手动对比员工ID的方式可以正常工作。我知道Filter方式效率更低,希望找出Joiners的使用错误。
失效的Joiners版本约束函数
Constraint unavailableEmployee (ConstraintFactory constraintFactory) { return constraintFactory.forEach(Shift.class) .join(Availability.class, Joiners.equal(Shift::getEmployee,Availability::getEmployee)) .filter((shift,availability) -> availability.getAvailabilityType() == AvailabilityType.UNAVAILABLE) .penalize(HardMediumSoftScore.ONE_HARD) .asConstraint("Unavailable employee"); }
可行的Filter版本约束函数
Constraint unavailableEmployee(ConstraintFactory constraintFactory) { return constraintFactory.forEach(Shift.class) .join(Availability.class) .filter((shift, availability) -> (shift.getEmployee().getEmployeeId().equals(availability.getEmployee().getEmployeeId()) && availability.getAvailabilityType() == AvailabilityType.UNAVAILABLE)) .penalize(HardMediumSoftScore.ONE_HARD) .asConstraint("Unavailable employee"); }
关联的领域类代码
Shift类
package com.guidematcher.domain; import lombok.Getter; import lombok.Setter; import org.optaplanner.core.api.domain.entity.PlanningEntity; import org.optaplanner.core.api.domain.lookup.PlanningId; import org.optaplanner.core.api.domain.variable.PlanningVariable; import java.time.LocalDateTime; import java.util.UUID; @PlanningEntity public class Shift { @Getter @Setter @PlanningId private String shiftId; @Getter @Setter private LocalDateTime shiftStart; @Getter @Setter private LocalDateTime shiftEnd; @Getter @Setter private EmployeeType requiredEmployeeType; @Getter @Setter private String requiredEmployeeTypeDesc; @Getter @Setter @PlanningVariable private Employee employee; public Shift() {} public Shift (LocalDateTime shiftStart,LocalDateTime shiftEnd, String requiredEmployeeTypeDesc) { this(UUID.randomUUID().toString(),shiftStart,shiftEnd,requiredEmployeeTypeDesc,null); } public Shift (String shiftId,LocalDateTime shiftStart,LocalDateTime shiftEnd,String requiredEmployeeTypeDesc, Employee employee) { this.shiftId = shiftId; this.shiftStart = shiftStart; this.shiftEnd = shiftEnd; this.setRequiredEmployeeTypeDesc(requiredEmployeeTypeDesc); this.employee = employee; } public void setRequiredEmployeeTypeDesc(String requiredEmployeeTypeDesc) { this.requiredEmployeeTypeDesc = requiredEmployeeTypeDesc; this.requiredEmployeeType = EmployeeType.getEmployeeTypeByDesc(requiredEmployeeTypeDesc); } public String toString() { return this.getShiftId() + "-" + this.getRequiredEmployeeType().getEmployeeTypeDesc() + " " + this.getShiftStart(); } }
Availability类
package com.guidematcher.domain; import lombok.Getter; import lombok.Setter; import org.optaplanner.core.api.domain.lookup.PlanningId; import java.time.LocalDate; import java.util.UUID; public class Availability { @Getter @Setter @PlanningId private String availabilityId; @Getter @Setter LocalDate availabilityDate; @Getter @Setter AvailabilityType availabilityType; @Getter @Setter Employee employee; public Availability() {} public Availability(String availabilityId, Employee employee, LocalDate availabilityDate, AvailabilityType availabilityType ) { this.availabilityId = UUID.randomUUID().toString(); this.employee = employee; this.availabilityDate = availabilityDate; this.availabilityType = availabilityType; } }
问题原因与解决方案
问题核心在于Employee类未正确重写equals()和hashCode()方法:
- Joiners.equal()依赖对象的equals比较逻辑,默认的Object.equals()仅比较对象引用地址;
- Filter方式是基于employeeId的值比较,能匹配同一个员工的不同实例;但Joiners用Employee对象比较时,只有当两个对象是内存中的同一个引用才会匹配,实际场景中Shift和Availability关联的Employee可能是不同实例但ID相同,导致关联失效。
解决方案:给Employee类重写equals()和hashCode(),基于employeeId实现值比较:
import java.util.Objects; // 在Employee类中添加以下方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return Objects.equals(getEmployeeId(), employee.getEmployeeId()); } @Override public int hashCode() { return Objects.hash(getEmployeeId()); }
修改后,Joiners.equal(Shift::getEmployee, Availability::getEmployee)就能正确匹配同一个员工的Shift和Availability记录,约束会正常生效。
内容的提问来源于stack exchange,提问作者Brendan Haggerty
相关产品推荐
相关产品推荐

