How can I write Queries for entities containing a 1-n reference?
Based on the spring-data-jdbc examples I will explain it with the following unit-test:
@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();
}
This models a LegoSet
referencing 2 Model
s. The repository.findByName()
is annotated with a custom query; the repository.findAll()
is the standard spring-boot-data method of the CrudRepository
(just as reference).
The custom query in version 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);
In this version the test fails w/
java.lang.AssertionError: [model must not be null]
Expecting actual not to be null
Okay, after that I add another JOIN
to 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);
Now the test fails w/
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
So how do I have to write that query correctly?
The first query is actually the correct one and it is working fine.
The problem is in your test. models
is a Map
, but you treat it like a List
which compiles because a list index is also a valid map key.
If you change the last two assertions in your test like this they will succeed:
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();
// ----------------------------------------^
An alternative is to use the second query but with a custom ResultSetExtractor
to collect multiple rows for the multiple models into a single LegoSet
.
I'm in a similar situation, but i don't see what is different in my situation.
I have three models: Client: id, name.. Project: id, name, clientId, List ProjectMember
Here is my had coded query
@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);
As you can see i'm looking for project where client name (using a join) match a pattern.
Executing the request by hand give me the expected results. But going through spring data jdbc give me no results
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.