【问题标题】:How to write an efficient JPQL query that returns list of linked records in a table?如何编写返回表中链接记录列表的高效 JPQL 查询?
【发布时间】:2016-06-22 07:58:35
【问题描述】:
这是我的后端 JPA(ORM) 结构:
MessageObject.java
messageId
messageName
relatedMessageId // maps to NEXT message in chain
假设消息名称中有一些内容表明它是我应该在链中预期的第一条消息(即 messageName 是“创建消息”)。
我将如何编写一个 JPQL 查询,该查询将根据名称为“创建消息”的消息按顺序返回消息,并根据初始结果中的 referencedId 值返回下一个消息?是否有更有效的方法可以使用流来执行此操作,或者我的数据模型结构中是否存在缺陷?
【问题讨论】:
标签:
java
jpa
data-structures
linked-list
【解决方案1】:
这与其说是 JPQL 问题,不如说是数据库设计问题。它会因数据库而异,但通常关系数据库没有基于递归父级的简单方法。
有几种方法可以解决这个问题:
------------------------------------------------------------------
| messageId | messageName | relatedMessageId | rootId | sequence |
------------------------------------------------------------------
| 1 | create | null | 1 | 1 |
| 2 | process | 1 | 1 | 2 |
| 3 | ship | 2 | 1 | 3 |
------------------------------------------------------------------
使用这样的表格,您可以轻松获取“其中 relatedMessageId 为空”的所有创建消息。并且如果您只需使用 rootId 和序列将它们全部选中,就需要链中的所有消息。
----------------------------------------------------------------
| messageId | messageName | relatedMessageId | path |
----------------------------------------------------------------
| 1 | create | null | 000>001 |
| 2 | process | 1 | 000>001>002 |
| 3 | ship | 2 | 000>001>003 |
----------------------------------------------------------------
这种方法有点古怪且难以维护,但在速度方面有一些好处。这个想法是在 varchar 字段中保留每条记录中的 id 路径。有了这个,您可以选择“where path LIKE '000>%' ORDER BY path”,它将在树状列表中按顺序选择所有记录。要获得单个序列,您只需将类似查询更改为“where path LIKE '000>001>%' ORDER BY path”。同样,这种方法很古怪,很难正确维护路径,但它可以在稍后获得回报,因为您可以使用单个查询而不是多个查询来选择所有子节点。
【解决方案2】:
对...有多种选择。我在这里说一个。
我了解您有一个“消息”表,其中包含一个 messageId 和一个 relatedMessageId 列。您的表将与自身存在关联,即 relatedMessageId 是 messageId 的外键。
你的实体看起来像这样......
@Entity
public class Transaction implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer messageId;
@OneToMany(mappedBy = "relatedMessage")
private List<Message> relatedMessages = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "relatedMessageId")
private Message parentMessage;
// constructors, aux methods, getter and setters
现在,您的 JQL 看起来像...
select m from Message m where m.parentMessage IS NULL
这将带来所有热门消息。从那里你可以去......
List<Message> parentMessages = messageRepoInstance.findParentMessages();
for(Message message : parentMessages) {
List<Message> relatedMessages = message.getRelatedMessages();
}
对不起,这都是我的想法,但我相信我设法通过了这个想法。 :)