I have Entity A related 1-1 with Entity B.
Entity B is related 1-1 with Entity C.
- UserEntity
- BadgeEntity
- BadgeTypEntity
- BadgeEntity
I want implements a complex filtered search using Specifications. Furthermore I would configure JOIN FETCH from UserEntity to BadgeEntity and from BadgeEntity to BadgeTypEntity, to retrieve UserEntity with its "BadgeType" in a single query.
Without specifications, using directly criteria API, it's simple to solve configuring the desired fetch where needed.
I first tried: https://github.com/tkaczmarzyk/specification-arg-resolver#enabling-spec-annotations-in-your-spring-app but it seems that is possible to configure only one level of fetching (UserEntity --> BadgeEntity)
So, I tried providing a "fake" Specification, that simply add desired JOIN FETCH to the query, but I suppose this a bad work around to make it work.
The User Entity
public class UserEntity
{
private String name;
private String email;
@Fetch(FetchMode.JOIN)
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "BADGE_ID")
private BadgeEntity badge;
...
}
The Badge Entity
public class BadgeEntity
{
@Fetch(FetchMode.JOIN)
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "BADGE_TYPE_ID")
private BadgeTypeEntity badgeType;
...
}
The BadgeType Entity
public class BadgeTypeEntity
{
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name = "BADGE_TYPE_ID")
private Integer badgeTypeId;
@Column(name = "CODE")
private String code;
}
The EMAIL Filter Specficication
public class UsersEmailSpec implements Specification<UserEntity>
{
private String email;
public UsersEmailSpec(String email)
{
this.email = email;
}
@Override
public Predicate toPredicate(Root<UserEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder)
{
Predicate result = null;
if(StringUtils.isNotEmpty(this.email))
{
result = criteriaBuilder.like(root.get(UserEntity_.email),this.email);
}
return result;
}
}
The NAME filter Specification
public class UsersNameSpec implements Specification<UserEntity>
{
private String name;
public UsersNameSpec(String name)
{
this.name = name;
}
@Override
public Predicate toPredicate(Root<UserEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder)
{
Predicate result = null;
if(StringUtils.isNotEmpty(this.name))
{
result = criteriaBuilder.like(root.get(UserEntity_.name),this.name);
}
return result;
}
}
The FAKE Specification to configure FETCH JOIN
Specification conjunctionSpec = new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
Fetch fetch = root.fetch(UserEntity_.badge, JoinType.INNER).fetch(BadgeEntity_.badgeType, JoinType.INNER);
return null;
}
};
Is there a way to accomplish this issue smartly using Specifications?