Java Spring引导-搜索类别树
我正在编写SpringBoot应用程序,它基本上是广告服务。在数据库中(我使用PostgreSQL方言),我存储类别树。每个类别都包含对其直接父类的引用。每个广告都指定了其类别,为了解决此问题,您只能将广告指定给叶类别 下面的图像捕捉到了结构 现在我需要从数据库中检索特定类别的广告。但假设我想检索E类的所有广告,这意味着我想检索E类的子类广告,但不一定是直接子类广告。在本例中,我想检索类别G、H、I和J的广告,因为它们都是Leaf,它们都是类别E的子类 如何通过Spring存储库实现它?下面是本例的简化实体类 类别Java Spring引导-搜索类别树,java,spring,postgresql,spring-boot,repository,Java,Spring,Postgresql,Spring Boot,Repository,我正在编写SpringBoot应用程序,它基本上是广告服务。在数据库中(我使用PostgreSQL方言),我存储类别树。每个类别都包含对其直接父类的引用。每个广告都指定了其类别,为了解决此问题,您只能将广告指定给叶类别 下面的图像捕捉到了结构 现在我需要从数据库中检索特定类别的广告。但假设我想检索E类的所有广告,这意味着我想检索E类的子类广告,但不一定是直接子类广告。在本例中,我想检索类别G、H、I和J的广告,因为它们都是Leaf,它们都是类别E的子类 如何通过Spring存储库实现它?下面是
@Entity
@Table(name = "categories")
public class Category {
@Id
@Column(name = "cat_id")
private int id;
@Column(name = "cat_name", length = 50, nullable = false, unique = true)
private String name;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "cat_parent", referencedColumnName = "cat_id", nullable = false)
private Category parent;
...
}
。。。和广告
@Entity
@Table(name = "advertisements")
public class Advertisement {
@Id
@Column(name = "adv_id")
private long id;
@Column(name = "adv_title", length = 100, nullable = false)
private String title;
@Column(name = "adv_description", length = 5000, nullable = false)
private String description;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "adv_category", referencedColumnName = "cat_id", nullable = false)
private Category category;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "adv_owner", referencedColumnName = "user_id", nullable = false)
private User owner;
...
}
这可能不是最优雅的方法,但您可以先收集类别包,然后使用in子句查询广告存储库,该子句将结果限制为仅在这些类别中的广告 首先将父/子关联的另一端添加到Category对象(如果还没有),然后创建一个方法,返回一个包含该类别和所有子对象(不仅仅是直接子对象)的集合,如下所示:
@JoinColumn(name = "cat_parent")
@OneToMany
Set<Category> children;
...
public Set<Category> collectLeafChildren() {
Set<Category> results = new HashSet<>();
if (children.isEmpty()) {
results.add(this);
} else {
children.forEach(child -> {
results.addAll(child.collectLeafChildren());
});
}
return results;
}
并使用以下各项:
Set<Category> categories = category.collectLeafChildren();
List<Advertisement> advertisementRepository.findByCategoryIn(categories);
Set categories=category.collectLeafChildren();
列出advertisementRepository.findByCategoryIn(类别);
您可以使用Postgres公共表表达式(CTE)编写本机查询。看这本书。@manish我从来没用过,我会查一查。我想这与那些递归查询有关吧?这看起来并不容易。有两种方法可以创建分层数据结构。当结构中的对象持有对其直接父对象的引用时,该结构称为邻接列表
。另一个选项是将结构建模为嵌套集
。在嵌套集中查找叶节点非常简单,需要一个非常简单的SQL查询。然而,嵌套集的构造非常复杂,只有当层次结构树基本上是静态的并且不需要经常更改时,嵌套集才实际有用。请仔细阅读这两个模型,看看嵌套集是否能解决您的问题。@manish好吧,一旦我创建了类别树,我可能不会更改它。这只是我硕士论文的一个项目,不需要太完美。所以nestedset
听起来像是可行的解决方案。不过,邻接树
似乎要优雅得多。我找到了一个本机SQL查询来从根目录检索所有叶,但我未能修改它,使其返回任何子树的叶节点。这似乎是最简单的解决方案,一点也不坏。这个项目不需要最好的。问题是,我已经映射了关联的另一端(父级)。我听说两头都要交往是不好的。我可以解除父母的职务,但我不确定将来是否需要……很高兴知道,我不确定为什么持有协会的两端会成为一个问题?我已经做过几次了,而且侥幸逃脱了;)出于性能考虑,默认情况下,父关系(ManyToOne)是急切加载的,而子关系(OneToMany)是惰性的。在queryI dunno中,您可以使用实体图或连接抓取来进一步控制这些关系的映射方式,我只是想了解JPA的内容,但我的老师曾经告诉我,我们应该避免保持关联的两端,一个就够了。顺便说一下,我需要稍微改变一下你的方法-我只需要给一个孩子的叶子类别,我不需要介于两者之间的类别public Set collectLeafChildren(){Set results=new HashSet();if(children.isEmpty()){results.add(this);}else{children.forEach(child->{results.addAll(child.collectLeafChildren();});}返回结果;}
很抱歉格式化。我想维护两端会有点繁琐。例如,如果要将新的子项添加到父项,则应确保该子项引用父项,并确保该子项位于子项的父项包中。使用新的代码示例(仅leafs)@Patryk更新了答案-您是否最终使用了此解决方案或其他解决方案来解决您的问题?如果对你有帮助,请接受答案,谢谢!
Set<Category> categories = category.collectLeafChildren();
List<Advertisement> advertisementRepository.findByCategoryIn(categories);