【问题标题】:Find entity by exact matching in collection在集合中通过精确匹配查找实体
【发布时间】:2020-10-10 18:20:49
【问题描述】:

我有这样的实体:

@Getter
@Setter
@Entity
public class Conversation extends AbstractEntity{

    @ElementCollection
    @Column(name = "user_id", nullable = false)
    @CollectionTable(name = "conversation_user", joinColumns = @JoinColumn(name = "conversation_id", nullable = false))
    private List<String> usersIds;
}

是否可以通过精确匹配用户 id 通过 spring 的存储库找到对话实体?例如我有这些实体:

 id | user_ids
------------------------------------------
 1  | user-a, user-b, user-c
 2  | user-a, user-b
 3  | user-a, user-c

因此,当我希望通过用户 ID user-auser-c 像这样的常规 IN 子句找到对话时:

SELECT c FROM Conversation c WHERE c.userIds IN :userIds

会找到 id 1 和 3 的对话,但我想找到完全匹配的,所以我的预期结果只有对话 3。

可能的解决方案是在存储库中使用常规 IN 子句,并在服务层中使用下一个过滤器集合,但我更喜欢直接从数据库返回所需实体的解决方案。至少在 JPQL 或本机 sql 中是否有可能?谢谢。

【问题讨论】:

  • 好吧,您使用的是“IN”,它基本上是一个“子集”运算符。等于(“=”)有效吗?我现在无法测试这个
  • @BenjaminMaurer 我今天试过了,它在应用程序启动时出现错误“用户 ID 上的运算符包含需要一个标量参数”

标签: spring-data-jpa hql jpql spring-repositories


【解决方案1】:

确保 user_ids 列中的用户 ID 按字母顺序排列。因此,例如当用户 b 进入 id 为 3 的对话时,user_ids 列中的 'user-a, user-c' 变为 'user-a, user-b, user-c'。

接下来,请确保当您要根据参与者的完全匹配检索会话时,查询参数中的用户 ID 也按字母顺序排列。 然后可以选择

select c from Converation c WHERE c.userIds = :userIds

现在只会找到完全匹配的内容。

【讨论】:

  • 与我给 Lavish 的答案相同.. 它给了我错误
【解决方案2】:

您可以在您的 Repository 类中编写自定义查询,如下所述:

@Repository
public interface YourRepository extends JpaRepository<ConversationModel, Integer> {

 @Query(nativeQuery = true, value = "select c from Converation c WHERE c.userId = :userId ORDER BY userId DESC LIMIT 1")
    Optional<ConversationModel> findByUserId(@Param("userId") String userId);

}

现在将从数据库层返回完全匹配的 userId。使其可选,并在服务层使用时检查返回值是否不为空

要获取多条记录,您必须在查询中使用 IN,如下所述

 @Query("select c from Converation c WHERE c.userIds IN :userIds")
    List<ConversationModel> findByUserIds(@Param("userIds") List<String> userIds);

希望这能解决您的问题。

【讨论】:

  • 你试过了吗?因为它给了我以下错误:org.springframework.dao.InvalidDataAccessApiUsageException:参数值 [cccccccc-000000000001] 与预期类型不匹配 [java.util.Collection (n/a)];嵌套异常是 java.lang.IllegalArgumentException:参数值 [cccccccc-000000000001] 与预期类型不匹配 [java.util.Collection (n/a)] 其中 cccccccc-000000000001 是传递到存储库的集合中的项目之一
  • 是的,我尝试过类似的查询。哦,明白了,我认为由于您的数据库返回的方法 1 有多个条目,我提到的方法 1 可能不起作用,因为它的返回类型是 Optional。您可能需要限制来自数据库层的结果,然后您可以使用类似`@Query(nativeQuery = true, value = "select c from Converation c WHERE c.userId = :userId ORDER BY userId DESC LIMIT 1")`
  • 如果你检查我的问题,有用户 id 的集合,而不是对话实体上的一个
  • 在这种情况下,IN 查询应该可以工作。我在我的项目中使用了类似的查询。
【解决方案3】:

使用HAVINGCASE 来计算匹配的userId 并检查等于搜索到的userIds 计数。

@Query(value = "SELECT c FROM Conversation c LEFT JOIN c.usersIds cu GROUP BY c "
           + "HAVING SUM(CASE WHEN cu IN (:userIds) THEN 1 ELSE -1 END) = :userIdsCount")
List<Conversation> findByUserIds(@Param("userIds") List<String> userIds,
                                 @Param("userIdsCount") Integer userIdsCount);

【讨论】:

  • 谢谢它的工作,但我希望 spring 对这个用例有更好的支持。
  • @DenisStephanov 这就是你可以使用 SQL 的方式,所以对于 JPQL,我认为 spring-data-jpa 尝试支持最常见的用例。精确的子匹配在 sql 中并不常见,也许这就是他们不为此提供任何关键字的原因。
猜你喜欢
  • 2018-01-18
  • 2012-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多