OptaPlanner和JAVA技术集成
文章目录
1. 概述
OptaPlanner的输入和输出数据(规划问题和最佳解决方案)是普通的JavaBeans(POJOs),因此与其他Java技术的集成非常简单。例如:
- 使用JPA注解为领域POJOs从数据库中读取规划问题(并将最佳解决方案存储在其中)。
- 使用JAXB注解为领域POJOs从XML文件中读取规划问题(并将最佳解决方案存储在其中)。
- 使用JAXB或Jackson注解为领域POJOs将Solver公开为REST服务,该服务从规划问题中读取并响应最佳解决方案,并在Camel或RESTEasy中连接Solver。
2. 持久化存储
2.1. 数据库:JPA和Hibernate
使用JPA注解为领域POJOs(解决方案、实体和问题事实)添加JPA注解,通过调用EntityManager.persist()将它们存储在数据库中。
不要混淆JPA的@Entity注解与OptaPlanner的@PlanningEntity注解。它们可以同时出现在同一个类上。
@PlanningEntity // OptaPlanner annotation
@Entity // JPA annotation
public class Talk {
...}
2.1.1. JPA和Hibernate:持久化分数
optaplanner-persistence-jpa jar为每种内置的分数类型提供了JPA分数转换器。
@PlanningSolution
@Entity
public class CloudBalance {
@PlanningScore
@Convert(converter = HardSoftScoreConverter.class)
protected HardSoftScore score;
...
}
请注意,这些转换器使得JPA和Hibernate将分数序列化为单个VARCHAR列。这样做的缺点是无法在SQL或JPA-QL查询中使用分数来有效地过滤结果,例如查询所有不可行的计划。
为了避免这个限制,可以实现CompositeUserType接口,将每个分数级别持久化到单独的数据库表列中。
2.1.2. JPA和Hibernate:规划克隆
在JPA和Hibernate中,大多数问题事实类通常都有一个从规划解决方案类到@ManyToOne关系。因此,问题事实类引用规划解决方案类,这意味着当解决方案进行规划克隆时,它们也需要进行克隆。可以在每个问题事实类上使用@DeepPlanningClone注解来强制执行规划克隆:
@PlanningSolution // OptaPlanner annotation
@Entity // JPA annotation
public class Conference {
@OneToMany(mappedBy="conference")
private List<Room> roomList;
...
}
@DeepPlanningClone // OptaPlanner annotation: Force the default planning cloner to planning clone this class too
@Entity // JPA annotation
public class Room {
@ManyToOne
private Conference conference; // Because of this reference, this problem fact needs to be planning cloned too
}
如果不这样做,可能导致持久化重复的解决方案、JPA异常或其他副作用。
2.2. XML或JSON:JAXB
使用JAXB注解为领域POJOs(解决方案、实体和问题事实)添加JAXB注解,以将它们序列化为/从XML或JSON。
添加依赖项optaplanner-persistence-jaxb jar以利用这些额外的集成功能:
2.2.1. JAXB:序列化分数
当通过默认的JAXB配置将分数序列化为XML或JSON时,会出现数据损坏的情况。要解决此问题,请配置适当的ScoreJaxbAdapter:
@PlanningSolution
@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD)
public class CloudBalance {
@PlanningScore
@XmlJavaTypeAdapter(HardSoftScoreJaxbAdapter.class)
private HardSoftScore score;
...
<