I have some already created org.springframework.data.jpa.domain.Specifications. Now I am creating a query in which I would like to use the specification on a table that I join to. But in order to use a Specification I need a Root, but joining gives me a Join object.
Is there a way of converting from a Join object to a Root? Or is there something analogous to Specification, but for Joins?
You don't need Root
object. Join
object is instance of Path
and Expression
interfaces. See example with working with join from Specification:
class JoinedSpecification extends Specification<JoinedEntity>() {
public Predicate pathPredicate(Path<JoinedEntity> joinedEntity, CriteriaQuery<?> query, CriteriaBuilder builder) {
return builder.equal(joinedEnity.get(JoinedEntity_.value), 20L);
}
@Override
public Predicate toPredicate(Root<JoinedEntity> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
return pathPredicate(root, query, builder);
}
}
class MySpecification extends Specification<Entity>() {
private static JoinedSpecification joinedSpecification = new JoinedSpecification();
@Override
public Predicate toPredicate(Root<Entity> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Join<T, JoinedEntity> join = root.join(Entity_.joinedEntity, JoinType.LEFT);
// Some join condition
Path<Long> someExpr = join.get(JoinedEntity_.someExpr);
Long someExprCriteria = 10L;
join = join.on(builder.equal(someExpr, someExprCriteria));
return joinedSpecification.pathPredicate(join, query, builder);
}
}
@Autowired
JpaSpecififcationExecutor<Entity> service;
Specification<Entity> spec = new MySpecification();
serivce.findAll(spec);
It will provide query like
SELECT e FROM Entity e LEFT JOIN e.joinedEntity j WITH j.someExpr=10 WHERE j.value = 20;
Tarwirdur Turon's solution does not fit my need so I managed to turn a Join
into a Root
by creating a Root<T>
implementation that delegates all methods to a Join<?,T>
instance. (Join and Root being children interfaces of From)
Although it works, it looks very dirty to me.
Tarwirdur Turon's solution doesn't work for me because I have an already built Specification<JoinedEntity>
and I want to find all Entity
for which the joinedEntity matches the specification without knowing what's 'inside' this specification.
public class JoinRoot<T> implements Root<T> {
private final Join<?, T> join;
public JoinRoot(Join<?, T> join) {
this.join = join;
}
// implements all Root methods, delegating them to 'this.join' (#boilerplate),
// cast when needed
@Override
public EntityType<T> getModel() {
// this one is the only one that cannot be delegated, although it's not used in my use case
throw new UnsupportedOperationException("getModel cannot be delegated to a JoinRoot");
}
}
Then use this class like follow :
Specification<JoinedEntity> joinedSpecs = ...
Specification<Entity> specs = (root, query, builder) -> {
// Convert Join into Root using above JoinRoot class
Root<JoinedEntity> r = new JoinRoot<>(root.join(Entity_.joinedEntity));
return joinedSpecs.toPredicate(r, query, builder);
}
Specification<Entity> where = Specifications.where(specs);
List<Entity> entities = entityRepository.findAll(where);
I really wonder why the Specification.toPredicate
method takes a Root<X>
as first argument instead of a From<Z,X>
, this would ease all the thing ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With