问题
I have a User
entity with skills
property as a type List. I want to query the User
table against a list of skills in such a way that if all the skills are present in skill column then only a match is found unless no.
I have used JPQL for this but it matches each element in the list one by one using the IN
clause.
User Class
@Entity(name = "App_User")
//table name "user" is not allowed in postgres
public class User {
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
@Column(name = "user_id", updatable = false, nullable = false)
@Setter(AccessLevel.NONE)
private UUID id;
@Column(name = "user_name")
@NotBlank(message = "Name is mandatory")
private String name;
@Column(name = "user_email")
@NotBlank(message = "Email is mandatory")
private String email;
// Current point balance of the user
@Column(name = "points")
private int points;
@ElementCollection(fetch = FetchType.EAGER)
@Column(name = "skills")
@NotEmpty
private List<String> skills = new ArrayList();
}
JPA query that I have used is
SELECT u FROM App_User u JOIN u.skills skill where skill in :skillList
If I want to match a list of skills like this Arrays.asList("skill1","skill2","skill3")
then I want only those users in the result who have all of these skills, not one or two. Above used IN
clause return the same result.
I have read that it is not possible to compare two lists in JPQL so how can I achieve this using CriteriaBuilder
CriteriaQuery
API?
回答1:
You can do this
@Query(value = "SELECT u FROM User u LEFT JOIN u.skills sk WHERE sk IN :skillList"
+ " GROUP BY u HAVING COUNT( sk) = :skillListSize")
List<User> findBySkills(@Param("skillList") List<String> skills,
@Param("skillListSize") long skillListSize);
Here, group by user and then check group having all skills or not using size. So it will fetch all user having all skills those are given.
Or use this way
@Query(value = "SELECT u FROM User u LEFT JOIN u.skills sk GROUP BY u"
+ " HAVING SUM(CASE WHEN sk IN (:skillList) THEN 1 ELSE 0 END) = :skillListSize")
List<User> findBySkills(@Param("skillList") List<String> skills,
@Param("skillListSize") long skillListSize);
And if you want a solution for user having exact same skill not more than the given list then see this solution.
回答2:
The problem that you want to solve is called Relational Division
.
SELECT u.* FROM App_User u
INNER JOIN
(
SELECT skills FROM App_User WHERE skills IN (list values)
GROUP BY skills
HAVING COUNT(DISTINCT skills) = (size of list)
) w ON u.user_name = w.user_name
来源:https://stackoverflow.com/questions/62733249/how-to-match-a-list-exactly-with-all-values-present-in-jpa-hibernate