在我的数据模型中,有一个递归的实体“位置”。此外,还与其他实体有关系。
相应的JPA(Spring Data JPA)实体如下所示:
@Entity
@Table(name = "location")
class Location{
@OneToMany(mappedBy = "parent", orphanRemoval = true)
@OrderBy("name ASC")
Set<Location> children = null
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "parent_id")
Location parent = null
@Column(name = "name")
String name = null
@OneToMany(mappedBy = "location", fetch = FetchType.EAGER)
Stops stops = null
...
执行只读查询的最佳方法是什么?我只需要具有完整递归结构的实体(表位置)内的信息,但不需要来自相关实体的信息。
我已经阅读了短语DTO投影,但没有关于如何处理递归结构。
答案 0 :(得分:0)
读取递归结构通常是通过使用SQL称为递归CTE 来完成的。 JPA不立即支持该功能,因为并非所有RDBMS都支持它。如果您知道您的DBMS支持它,则可以使用以下SQL来做到这一点:
WITH RECURSIVE nodes(id, parent_id) AS (
SELECT id, parent_id FROM location l where id = ?
UNION ALL
SELECT l.id, l.parent_id FROM nodes n JOIN location l ON n.parent_id = l.id
)
SELECT id, parent_id FROM nodes
通过该操作,您将获得一个特定的父级位置ID列表以及它们各自的父级ID。您必须将结构引入其中。
List<Object[]> result = //get the result of the query
Map<Integer, LocationDto> locationMap = new HashMap<>();
result.forEach(r -> locationMap.put(result.get(0), new LocationDto(result[0], result[1])));
locationMap.values().forEach(l -> l.setParent(locaitonMap.get(l.getParentId())));
如果由于可移植性问题而不想使用纯SQL,或者只是因为不想放弃抽象而使用,可以使用Blaze-Persistence,它在JPA之上并增加了对CTE的支持。具有持久性的查询看起来像这样
List<LocationCte> result = criteriaBuilderFactory.create(entityManager, LocationCte.class)
.withRecursive(LocationCte.class)
.from(Location.class, "l")
.bind("id").select("l.id")
.bind("parent").select("l.parent.id")
.where("id").eq(initialId)
.unionAll()
.from(Location.class, "l")
.innerJoinOn(LocationCte.class, "cte")
.on("cte.parent").eqExpression("l.id)
.end()
.bind("id").select("l.id")
.bind("parent").select("l.parent.id")
.end()
.from(LocationCte.class)
.getResultList();
您还将需要此特殊实体类
@CTE
@Entity
public class LocationCte {
@Id Integer id;
Integer parent;
}