Spring 带有严格子句的数据
Spring Data with strict In-clause
请参阅下面更新的问题
我想查询每个元素都需要具有所有给定属性的元素,例如要退回的一套,不是只有一个。
默认Spring数据Jpa查询,其中一个属性就够了:
findAllByAttributeIn(Set<Attribute> setAttr)
例题
对于这些元素(抽象的,没有实际的table)
id | attr
----------
1 | A
2 | A,B
3 | A,B,C
使用此过滤器:
setAttr: [A,B]
默认查询
findAllByAttributeIn(Set<Attribute> setAttr)
returns 所有元素(按 id):
[1,2,3]
想要的结果只有第二个元素([A,B])。
是否可以使用给定的 Spring Data Jpa 查询关键字或者是创建自定义查询的唯一解决方案?
更新问题:
有两个实体,媒体和标签。他们有一个 多对多 关系,在 table Media_has_tags 中实现。每个媒体都可以有任意数量的标签 (0..*)。所以在我的 Spring 应用程序中,媒体有一组标签作为属性,反之亦然。 类:
Media = {
id: string,
Set<Tag> tags,
...
}
Tag = {
id: string,
Set<Media> medias,
...
}
对应的table是:
Media
-------
id | ...
Tag
-------
id | ...
title | ...
Media_has_tags
-------
media_id | tag_id
现在我有一组标签,我想通过这些标签获取所有具有该组标签的所有媒体。他们当然可以有更多标签,但他们需要拥有我提供的集合中的每个标签。
具体例子:
Media
-------
id | ...
-------
1 |
2 |
Tag
-------
id | title | ...
1 | A |
2 | B |
Media_has_tags
-------
media_id | tag_id
1 | 1
2 | 1
2 | 2
给定一组标签 [A, B] 结果我只想要 ID 为 2 的媒体,因为 ID 为 1 的媒体没有标签 'B'.
我可以使用关键字实现吗?如果,我必须如何构建自己的查询?
抱歉,您的问题最初是错误的。
findAllByAttributeIn(Set<Attribute> setAttr)
生成一个 SQL 请求,如下所示:
select * from <table_name> t where t.attribute in (?,?,?...?)
因此,根据 RDBMS-rules,您的结果是正确且符合预期的。
您的 abstract 表的真实结构是什么?
id | attr
----------
1 | A
2 | A,B
3 | A,B,C
现实生活中应该是这样的:
id | attr
----------
1 | A
2 | A
2 | B
3 | A
3 | B
3 | C
再一次 - 在这种情况下,您得到的结果对于任何 RDBMS 都是可行的。
请给我们您项目中的真实示例
因问题更新而更新:
好的,问题已经说明了。
在这种情况下,没有直接的方法可以使用标准的 CrudRepository 语法来解决它。但是你可以尝试写 @Query
请求来设法达到目标。
显然 SQL 这个问题必须通过使用 group by
和 having
来解决。它会是这样的:
select media_id
From media_has_tags
Where tag_id in(1,2 3)
Group by media_id
Having count(*)=3
就 SpringData 而言,这意味着您必须创建 MediaHasTagsRepository
接口和适当的查询方法来获取查找的 ID。之后,您可以轻松找到所需的媒体。
但恕我直言,这种方法看起来不太好。我认为最好的方法是通过您的初始查询找到所有媒体,而不是通过 Java 中的给定条件过滤它们。
例如。你有一个 List<Media>
,其中每个元素至少有一个你正在寻找的标签。然后我们可以循环查找包含所有查找标签的媒体:
List<Media> list; // here is a filled list of medias
Set<String> titles; // here is a set of title interseptions we a looking for
final List<Media> result = new ArrayList<>();
for (Media media: list) {
if (!list.getTags().isEmpty()){
Set<String> tagTitles = list.getTags().stream().map(item -> item.getTitle()).filter(title -> titles.contains(title)).distinct().collect(Collectors.toSet());
if (tagTitles.size() == titles.size()) {
result.add(media);
}
}
}
请参阅下面更新的问题
我想查询每个元素都需要具有所有给定属性的元素,例如要退回的一套,不是只有一个。
默认Spring数据Jpa查询,其中一个属性就够了:
findAllByAttributeIn(Set<Attribute> setAttr)
例题
对于这些元素(抽象的,没有实际的table)
id | attr
----------
1 | A
2 | A,B
3 | A,B,C
使用此过滤器:
setAttr: [A,B]
默认查询
findAllByAttributeIn(Set<Attribute> setAttr)
returns 所有元素(按 id):
[1,2,3]
想要的结果只有第二个元素([A,B])。
是否可以使用给定的 Spring Data Jpa 查询关键字或者是创建自定义查询的唯一解决方案?
更新问题:
有两个实体,媒体和标签。他们有一个 多对多 关系,在 table Media_has_tags 中实现。每个媒体都可以有任意数量的标签 (0..*)。所以在我的 Spring 应用程序中,媒体有一组标签作为属性,反之亦然。 类:
Media = {
id: string,
Set<Tag> tags,
...
}
Tag = {
id: string,
Set<Media> medias,
...
}
对应的table是:
Media
-------
id | ...
Tag
-------
id | ...
title | ...
Media_has_tags
-------
media_id | tag_id
现在我有一组标签,我想通过这些标签获取所有具有该组标签的所有媒体。他们当然可以有更多标签,但他们需要拥有我提供的集合中的每个标签。
具体例子:
Media
-------
id | ...
-------
1 |
2 |
Tag
-------
id | title | ...
1 | A |
2 | B |
Media_has_tags
-------
media_id | tag_id
1 | 1
2 | 1
2 | 2
给定一组标签 [A, B] 结果我只想要 ID 为 2 的媒体,因为 ID 为 1 的媒体没有标签 'B'.
我可以使用关键字实现吗?如果,我必须如何构建自己的查询?
抱歉,您的问题最初是错误的。
findAllByAttributeIn(Set<Attribute> setAttr)
生成一个 SQL 请求,如下所示:
select * from <table_name> t where t.attribute in (?,?,?...?)
因此,根据 RDBMS-rules,您的结果是正确且符合预期的。 您的 abstract 表的真实结构是什么?
id | attr
----------
1 | A
2 | A,B
3 | A,B,C
现实生活中应该是这样的:
id | attr
----------
1 | A
2 | A
2 | B
3 | A
3 | B
3 | C
再一次 - 在这种情况下,您得到的结果对于任何 RDBMS 都是可行的。 请给我们您项目中的真实示例
因问题更新而更新:
好的,问题已经说明了。
在这种情况下,没有直接的方法可以使用标准的 CrudRepository 语法来解决它。但是你可以尝试写 @Query
请求来设法达到目标。
显然 SQL 这个问题必须通过使用 group by
和 having
来解决。它会是这样的:
select media_id
From media_has_tags
Where tag_id in(1,2 3)
Group by media_id
Having count(*)=3
就 SpringData 而言,这意味着您必须创建 MediaHasTagsRepository
接口和适当的查询方法来获取查找的 ID。之后,您可以轻松找到所需的媒体。
但恕我直言,这种方法看起来不太好。我认为最好的方法是通过您的初始查询找到所有媒体,而不是通过 Java 中的给定条件过滤它们。
例如。你有一个 List<Media>
,其中每个元素至少有一个你正在寻找的标签。然后我们可以循环查找包含所有查找标签的媒体:
List<Media> list; // here is a filled list of medias
Set<String> titles; // here is a set of title interseptions we a looking for
final List<Media> result = new ArrayList<>();
for (Media media: list) {
if (!list.getTags().isEmpty()){
Set<String> tagTitles = list.getTags().stream().map(item -> item.getTitle()).filter(title -> titles.contains(title)).distinct().collect(Collectors.toSet());
if (tagTitles.size() == titles.size()) {
result.add(media);
}
}
}