【问题标题】:How to match a list exactly with all values present in JPA Hibernate?如何将列表与 JPA Hibernate 中存在的所有值完全匹配?
【发布时间】:2020-10-25 05:32:58
【问题描述】:

我有一个带有skills 属性的User 实体作为类型列表。我想根据技能列表查询User 表,如果技能列中存在所有技能,则除非没有,否则只会找到匹配项。

我为此使用了 JPQL,但它使用 IN 子句一一匹配列表中的每个元素。

用户类

@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 查询是

SELECT u FROM App_User u JOIN u.skills skill where skill in :skillList

如果我想匹配像Arrays.asList("skill1","skill2","skill3")这样的技能列表,那么我只希望结果中那些拥有所有这些技能的用户,而不是一两个。上面使用的IN 子句返回相同的结果。

我读到无法在 JPQL 中比较两个列表,那么如何使用 CriteriaBuilderCriteriaQueryAPI 来实现这一点?

【问题讨论】:

    标签: java spring hibernate jpa spring-data-jpa


    【解决方案1】:

    您要解决的问题称为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 
    

    【讨论】:

    • 由于我们的查询中有多个联接,这是可行的解决方案。
    【解决方案2】:

    你可以这样做

    @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);
    

    在这里,按用户分组,然后检查具有所有技能的组或不使用大小。所以它会获取所有拥有所有技能的用户。

    或者这样用

    @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);
    

    如果您想为具有完全相同技能的用户提供不超过给定列表的解决方案,请参阅此solution

    【讨论】:

    • 很好的解决方案。 Integer skillListSize需要更新Long skillListSize
    • @KavithakaranKanapathippillai 谢谢,考虑skills.size() ?
    • @KavithakaranKanapathippillai 更新了,实际上在发送值时这不是问题,您可以将其转换为整数。 (因为我根本不使用skills.size()
    • ` @Query(value = "SELECT u FROM App_User u LEFT JOIN u.skills sk WHERE sk IN :skillList" + " GROUP BY u HAVING COUNT(DISTINCT sk) = :skillListSize") 列表 findBySkills(@Param("skillList") List 技能,@Param("skillListSize") long SkillListSize); `
    • 谢谢@Eklavya,它适用于数据库中的现有数据。我在测试代码中使用了内存数据库,也许这就是它失败的原因。
    猜你喜欢
    • 1970-01-01
    • 2023-01-15
    • 1970-01-01
    • 1970-01-01
    • 2014-10-20
    • 2021-08-07
    • 2019-11-30
    • 1970-01-01
    相关资源
    最近更新 更多