I just started learning the use of Specification
in Spring queries. I have written a rather complex Specification that filters a database by a type. For demonstration I will show a simple version with the same idea:
Specification<Animal> animalWithType(AnimalType type) {
return (root, cq, cb) -> {
return cb.equal(root.get(Animal_.type), type);
};
}
I can call it like this animalRepository.findAll(animalWithType(AnimalType.Dog))
and I get all dogs.
Now I also have humans which have a favourite type of animal. I need to make a query where I get all humans for which we have an animal of their favourite type. For this I want to reuse the first specification, because it is a very complication one. So I want to do something like this:
Specification<Human> weHaveFavouriteAnimal() {
return (root, cq, cb) -> {
return cb.exists(animalWithType(root.get(Human_.favouriteAnimalType)));
};
}
I cannot do this for two reasons:
root.get(Human_.favouriteAnimalType)
will give meExpression<AnimalType>
which I cannot pass to myanimalWithType
function. I also don't have an easy way to convert a raw AnimalType toExpression<AnimalType>
and pass it then, because I need an Instance ofQueryBuilder
to do that.The function
animalWithType
returns a Specification but I need a Predicate. I assume I need to make aSubQuery
of some kind here, but I can't pass aSubQuery
to the functionSpecification::toPredicate
because it requires a CriteriaQuery.
Is there a way to reuse a Specification as an "exists" subquery in another Specification for a different Entity?
In SQL the first specification would do something like select * from animals where type == Dog
and the second one should have this one as a subquery and look something like this select * from humans where exists (select * from animals where type == humans.favouriteAnimalType)
. My SQL is a bit rusty but I hope this is not too far off. This seems like the first query is very well reused in the second query. I want to do something similar with Specifications.