spring-data-jdbc:包含具有 1-n 关系的实体的查询
spring-data-jdbc: query containing entity with a 1-n relation
如何为包含 1-n 引用的实体编写查询?
基于 spring-data-jdbc 示例,我将使用以下单元测试对其进行解释:
@Test
public void customQuery_ReferenceMultipleInstances() {
// prepare
LegoSet smallCar = createLegoSet("Small Car 01", 5, 12);
smallCar.setManual(new Manual("Just put all the pieces together in the right order", "Jens Schauder"));
smallCar.addModel("suv", "SUV with sliding doors.");
smallCar.addModel("roadster", "Slick red roadster.");
repository.save(smallCar);
// execute
List<LegoSet> actual = repository.findByName("Small Car 01");
Iterable<LegoSet> compare = repository.findAll();
// verify
assertThat(actual).as("same number of lego sets").hasSize(Lists.newArrayList(compare).size());
assertThat(actual.get(0).getModels()).as("same number of models").hasSize(Lists.newArrayList(compare).get(0).getModels().size());
assertThat(actual.get(0).getModels().get(0)).as("model must not be null").isNotNull();
assertThat(actual.get(0).getModels().get(0).getName()).as("model must have a name").isNotEmpty();
}
这模拟了一个 LegoSet
引用 2 Model
s。 repository.findByName()
用自定义查询注释; repository.findAll()
是 CrudRepository
的标准 spring-boot-data 方法(仅供参考)。
版本 1 中的自定义查询:
@Query("SELECT ls.id, ls.name, ls.min_age, ls.max_age, " +
"h.handbuch_id AS manual_handbuch_id, h.author AS manual_author, h.text AS manual_text " +
"FROM lego_set ls JOIN handbuch h ON ls.id = h.handbuch_id " +
"WHERE name = :name")
List<LegoSet> findByName(@Param("name") String name);
在此版本中,测试失败 w/
java.lang.AssertionError: [model must not be null]
Expecting actual not to be null
好吧,之后我再添加一个JOIN
到model
:
@Query("SELECT ls.id, ls.name, ls.min_age, ls.max_age, " +
"h.handbuch_id AS manual_handbuch_id, h.author AS manual_author, h.text AS manual_text, " +
"m.* " +
"FROM lego_set ls JOIN handbuch h ON ls.id = h.handbuch_id " +
"JOIN model m ON ls.id = m.lego_set " +
"WHERE name = :name")
List<LegoSet> findByName(@Param("name") String name);
现在测试失败了
java.lang.AssertionError: [same number of lego sets]
Expected size:<1> but was:<2> in:
<[LegoSet(id=1, name=Small Car 01, minimumAge=P5Y, maximumAge=P12Y, manual=Manual(id=1, author=Jens Schauder , text=Just put all the pieces together in the right order), models={suv=Model(name=suv, description=SUV with sliding doors.), roadster=Model(name=roadster, description=Slick red roadster.)}),
LegoSet(id=1, name=Small Car 01, minimumAge=P5Y, maximumAge=P12Y, manual=Manual(id=1, author=Jens Schauder
那么我该如何正确编写该查询?
第一个查询实际上是正确的,并且工作正常。
问题出在你的测试中。 models
是一个 Map
,但是你把它当作一个 List
来编译,因为列表索引也是一个有效的映射键。
如果您像这样更改测试中的最后两个断言,它们将会成功:
assertThat(actual.get(0).getModels().get("suv")).as("model must not be null").isNotNull();
assertThat(actual.get(0).getModels().get("suv").getName()).as("model must have a name").isNotEmpty();
// ----------------------------------------^
另一种方法是使用第二个查询,但 with a custom ResultSetExtractor
将多个模型的多行收集到单个 LegoSet
。
我也有类似的情况,但我看不出我的情况有什么不同。
我有三个模型:
客户:ID,姓名..
项目:id,名称,clientId,列表
项目成员
这是我的编码查询
@Query("""
select
project.id,
project.legacy_id,
project.code,
project.client_id,
project.is_archived,
project.name,
project.budget_type,
project.hours_budget_max,
project.money_budget_max
from project
join client on client.id = project.client_id
where client.name ilike '%:name%'
""")
List<Project> findByClientNameContainingIgnoreCase(final String name);
如您所见,我正在寻找客户名称(使用连接)匹配模式的项目。
手动执行请求给我预期的结果。但是通过 spring 数据 jdbc 没有给我任何结果
如何为包含 1-n 引用的实体编写查询?
基于 spring-data-jdbc 示例,我将使用以下单元测试对其进行解释:
@Test
public void customQuery_ReferenceMultipleInstances() {
// prepare
LegoSet smallCar = createLegoSet("Small Car 01", 5, 12);
smallCar.setManual(new Manual("Just put all the pieces together in the right order", "Jens Schauder"));
smallCar.addModel("suv", "SUV with sliding doors.");
smallCar.addModel("roadster", "Slick red roadster.");
repository.save(smallCar);
// execute
List<LegoSet> actual = repository.findByName("Small Car 01");
Iterable<LegoSet> compare = repository.findAll();
// verify
assertThat(actual).as("same number of lego sets").hasSize(Lists.newArrayList(compare).size());
assertThat(actual.get(0).getModels()).as("same number of models").hasSize(Lists.newArrayList(compare).get(0).getModels().size());
assertThat(actual.get(0).getModels().get(0)).as("model must not be null").isNotNull();
assertThat(actual.get(0).getModels().get(0).getName()).as("model must have a name").isNotEmpty();
}
这模拟了一个 LegoSet
引用 2 Model
s。 repository.findByName()
用自定义查询注释; repository.findAll()
是 CrudRepository
的标准 spring-boot-data 方法(仅供参考)。
版本 1 中的自定义查询:
@Query("SELECT ls.id, ls.name, ls.min_age, ls.max_age, " +
"h.handbuch_id AS manual_handbuch_id, h.author AS manual_author, h.text AS manual_text " +
"FROM lego_set ls JOIN handbuch h ON ls.id = h.handbuch_id " +
"WHERE name = :name")
List<LegoSet> findByName(@Param("name") String name);
在此版本中,测试失败 w/
java.lang.AssertionError: [model must not be null]
Expecting actual not to be null
好吧,之后我再添加一个JOIN
到model
:
@Query("SELECT ls.id, ls.name, ls.min_age, ls.max_age, " +
"h.handbuch_id AS manual_handbuch_id, h.author AS manual_author, h.text AS manual_text, " +
"m.* " +
"FROM lego_set ls JOIN handbuch h ON ls.id = h.handbuch_id " +
"JOIN model m ON ls.id = m.lego_set " +
"WHERE name = :name")
List<LegoSet> findByName(@Param("name") String name);
现在测试失败了
java.lang.AssertionError: [same number of lego sets]
Expected size:<1> but was:<2> in:
<[LegoSet(id=1, name=Small Car 01, minimumAge=P5Y, maximumAge=P12Y, manual=Manual(id=1, author=Jens Schauder , text=Just put all the pieces together in the right order), models={suv=Model(name=suv, description=SUV with sliding doors.), roadster=Model(name=roadster, description=Slick red roadster.)}),
LegoSet(id=1, name=Small Car 01, minimumAge=P5Y, maximumAge=P12Y, manual=Manual(id=1, author=Jens Schauder
那么我该如何正确编写该查询?
第一个查询实际上是正确的,并且工作正常。
问题出在你的测试中。 models
是一个 Map
,但是你把它当作一个 List
来编译,因为列表索引也是一个有效的映射键。
如果您像这样更改测试中的最后两个断言,它们将会成功:
assertThat(actual.get(0).getModels().get("suv")).as("model must not be null").isNotNull();
assertThat(actual.get(0).getModels().get("suv").getName()).as("model must have a name").isNotEmpty();
// ----------------------------------------^
另一种方法是使用第二个查询,但 with a custom ResultSetExtractor
将多个模型的多行收集到单个 LegoSet
。
我也有类似的情况,但我看不出我的情况有什么不同。
我有三个模型: 客户:ID,姓名.. 项目:id,名称,clientId,列表 项目成员
这是我的编码查询
@Query("""
select
project.id,
project.legacy_id,
project.code,
project.client_id,
project.is_archived,
project.name,
project.budget_type,
project.hours_budget_max,
project.money_budget_max
from project
join client on client.id = project.client_id
where client.name ilike '%:name%'
""")
List<Project> findByClientNameContainingIgnoreCase(final String name);
如您所见,我正在寻找客户名称(使用连接)匹配模式的项目。
手动执行请求给我预期的结果。但是通过 spring 数据 jdbc 没有给我任何结果