0
votes

I have Entity A related 1-1 with Entity B.
Entity B is related 1-1 with Entity C.

  • UserEntity
    • BadgeEntity
      • BadgeTypEntity

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?

1
Hey, did you achieve this ? - Jessy

1 Answers

0
votes

You can achieve multi-level entity child mapping.

Example:

criteriaBuilder.like(root.get("badge").get("badgeType").get("email"),this.email);