Issue
I have 3 entities with the below relationships.
@Table(name = "a")
@Entity
public class A{
@Id
@GeneratedValue
@Column(name = "a_uuid")
@OneToMany(mappedBy = "a", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<B> bList;
@OneToMany(mappedBy = "a", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<C> cList;
}
@Table(name = "b")
@Entity
public class B{
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(fetch = "a_uuid")
private A a;
}
@Table(name = "c")
@Entity
class C{
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(fetch = "a_uuid")
private A a;
}
I need to join the above three tables and I am using a separate specification for each table
@Component
public class SpecificationTableAUtil {
public Specification<A> tableACriteriaMatches(final SomeCriteria criteria) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicateList = new ArrayList<>();
predicateList .add(
criteriaBuilder.isNull(
root.get("some_column_in_table_a")));
Predicate[] predicates = new Predicate[predicateList.size()];
return criteriaBuilder.and(predicateList.toArray(predicates ));
};
}
}
}
@Component
public class SpecificationTableBUtil {
public Specification<A> tableBCriteriaMatches(final SomeCriteria criteria) {
return (root, query, criteriaBuilder) -> {
ListJoin<A, B> b =
root.joinList("bList", JoinType.LEFT);
List<Predicate> predicateList = new ArrayList<>();
predicateList .add(
criteriaBuilder.isNull(
b.get("some_column_in_table_b")));
Predicate[] predicates = new Predicate[predicateList.size()];
return criteriaBuilder.and(predicateList.toArray(predicates ));
};
}
}
}
From my Service Class,
@Service
public class ServiceClass{
//Autowire the utilities
public Specification<A> getSpecification(final SomeCriteria criteria) {
return bUtil.tableBCriteriaMatches(criteria)
.and(aUtil.tableACriteriaMatches(criteria));
}
}
}
Now I need to join Table C as well. How to implement this in a a better way? I need to have the lines of code per method to be as small as possible
Solution
I figured it out on my own. This is what I came up with.
@Component
public class SpecificationTableAUtil {
@Autowired
private SpecificationTableBUtil bSpecificationUtil;
public Specification<A> tableACriteriaMatches(final SomeCriteria criteria) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicateList = new ArrayList<>();
predicateList .add(
criteriaBuilder.isNull(
root.get("some_column_in_table_a")));
predicateList.add(bSpecificationUtil.criteriaMatches(criteria).toPredicate(root, query, criteriaBuilder));
Predicate[] predicates = new Predicate[predicateList.size()];
return criteriaBuilder.and(predicateList.toArray(predicates ));
};
}
}
}
For table B specification,
@Component
public class SpecificationTableBUtil {
public Specification<A> tableBCriteriaMatches(final SomeCriteria criteria) {
return (root, query, criteriaBuilder) -> {
ListJoin<A, B> b =
root.joinList("bList", JoinType.LEFT);
List<Predicate> predicateList = new ArrayList<>();
predicateList .add(
criteriaBuilder.isNull(
b.get("some_column_in_table_b")));
Predicate[] predicates = new Predicate[predicateList.size()];
return criteriaBuilder.and(predicateList.toArray(predicates));
};
}
}
}
From Service class,
@Service
public class ServiceClass {
@Autowired
private SpecificationTableAUtil aSpecificationUtil;
public Specification<A> getSpecification(final SomeCriteria criteria) {
return aSpecificationUtil.tableACriteriaMatches(criteria);
}
}
}
Answered By - Jammy