如何编写Spring Data JPA查询判断字符串包含列表中任意元素
问题解决方案
方案1:保留现有逗号分隔topics字段的实现
原有IN查询对逗号分隔字符串不生效,因为数据库会将整个topics字符串作为单个值匹配集合元素,不符合匹配需求。可通过调用数据库内置函数实现任意关键词匹配,以MySQL为例,查询写法如下:
@Query("select new com.fullstack.dtos.projections.LocalizedRecipe(t.id, t.created, t.createdBy, t.lastModified, t.lastModifiedBy, t.version, l.title, l.description) from tutorial t inner join t.localizations l where l.id.locale = :lang and EXISTS (SELECT 1 FROM :topics topic WHERE FUNCTION('FIND_IN_SET', topic, t.topics) > 0)") Page<LocalizedRecipe> findAllByTopics(String lang, Set<String> topics, Pageable page);
该方案缺点是无法走索引,数据量大时查询性能差,仅适合临时使用。
方案2:改用@ElementCollection集合存储(推荐)
首先调整实体类的topics字段定义:
// 替换原有private String topics; @ElementCollection(fetch = FetchType.LAZY) @CollectionTable(name = "recipe_topics", joinColumns = @JoinColumn(name = "recipe_id")) @Column(name = "topic") private Set<String> topics; // 用Set可自动去重,也可替换为List
你之前报错的原因是直接复用了t.topics in :topics的写法,IN语法要求左侧是单个元素、右侧是集合,而你左侧是集合类型,才会触发类型不匹配异常。正确的交集判断查询写法如下:
@Query("select new com.fullstack.dtos.projections.LocalizedRecipe(t.id, t.created, t.createdBy, t.lastModified, t.lastModifiedBy, t.version, l.title, l.description) from tutorial t inner join t.localizations l where l.id.locale = :lang and EXISTS (SELECT 1 FROM t.topics topic WHERE topic IN :topics)") Page<LocalizedRecipe> findAllByTopics(String lang, Set<String> topics, Pageable page);
该逻辑只要食谱的topics集合中存在任意一个传入的查询话题,就会命中结果,完全符合你的需求。同时可以给关联表recipe_topics的topic字段加索引,查询性能远高于字符串存储方案,后续话题增删也不需要处理字符串拼接拆分逻辑,可维护性更强。
内容的提问来源于stack exchange,提问作者Goncalo Condeco
相关产品推荐
相关产品推荐

