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?