How to join multiple columns using Specification in JPA?

左心房为你撑大大i 提交于 2019-12-11 16:24:29

问题


I'm trying to convert a query into a JPA Specification, the query contains JOIN operation with OR condition.

Here is the Query :

select u from User u inner join Operation o on (u.id = o.verificateur1 or u.id = o.verificateur2) where o.id not in (:ids)

I tried to write a specification but I'm blocked on how to join multiple column with OR condition.

public class UserSpecification {

    public static Specification<User> UsersNotInSelectedOperations(final List<Long> operationId ){

        return new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Join<User, Operation> userJoinOp = root.join("fk_user1_id");
                final Path<User> users = userJoinOp.get("id");
                return criteriaBuilder.not(users.in(operationId));
            }
        };
    }

}

the User entity :

@Entity
@Table(name = "aigle_user")
public class User extends AbstractEntity implements UserDetails {

    private static final long serialVersionUID = 2840226091237599675L;

    @Column(name = "mail", nullable = true)
    private String mail;

    @Column(name = "password")
    private String password;

    @Column(name = "is_activated")
    private boolean isActivated;

    @Column(name = "is_admin")
    private boolean isAdmin;

    @ManyToMany(cascade = { CascadeType.PERSIST })
    @LazyCollection(LazyCollectionOption.FALSE)
    @JoinTable(name = "aigle_group_user", joinColumns = @JoinColumn(name = "fk_user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "fk_group_id", referencedColumnName = "id"))
    private Set<Group> groups;

    @ManyToMany(cascade = { CascadeType.PERSIST })
    @LazyCollection(LazyCollectionOption.FALSE)
    @JoinTable(name = "aigle_role_user", joinColumns = @JoinColumn(name = "fk_user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "fk_role_id", referencedColumnName = "id"))
    private Set<Role> roles;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "destinataire", orphanRemoval = true)
    private Set<Tache> taches;

    @Column(name = "last_name", unique = false)
    private String lastName;

    @Column(name = "first_name", unique = false)
    private String firstName;

    @Column(name = "telephone")
    private String telephone;

    private String salesforceId;

-----
}

The Operation Entity

public class Operation extends OperationField {

...
    @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_user1_id")
    private User verificateur1;

    @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_user2_id")
    private User verificateur2;
.....
}

I expect a Specification that replace the query above


回答1:


You can try to use subquery instead of join expression

    public static Specification<User> UsersNotInSelectedOperations(final List<Long> operationIds) {
            return (root, query, builder) -> {
                    Subquery<Operation> subquery = query.subquery(Operation.class);
                    Root<Operation> operation = subquery.from(Operation.class);

                    Predicate predicate1_1 = builder.equal(operation.get("verificateur1").get("id"), root.get("id"));
                    Predicate predicate1_2 = builder.equal(operation.get("verificateur2").get("id"), root.get("id"));

                    Predicate predicate1 = builder.or(predicate1_1, predicate1_2); 
                    Predicate predicate2 = operation.get("id").in(operationIds).not();

                    subquery.select(operation).where(predicate1, predicate2);

                    return builder.exists(subquery);
            };
   }

Using this specification you get HQL query like this

from User u
where exists( // subquery ->
  from Operation o
  where (o.verificateur1.id = u.id or o.verificateur2.id = u.id) // predicate 1
  and o.id not in (:ids) // predicate 2
  // <- subquery
)

from User u is shorter replacement for select u from User u

I wrote this code without running, so it needs revision



来源:https://stackoverflow.com/questions/58096427/how-to-join-multiple-columns-using-specification-in-jpa

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!