Spring Data JPA - 如何在同一列上使用多个值实现 LIKE 搜索
Spring Data JPA - How to implement LIKE search with multiple values on the same column
假设我有以下 table、
MOVIE_TITLE
- 蝙蝠侠前传
- 蝙蝠侠Returns
- Return 绝地
- 指环王: Return 之王
- 恋爱中的莎士比亚
- 死亡诗社
- 死侍
- 指环王:护戒使者
我想实现对电影标题的搜索并传递逗号分隔值(使用多个关键字搜索)
例如,
搜索文字:蝙蝠侠
结果:记录 #1,2
搜索文字:蝙蝠侠,Return
结果:记录 #1,2,3,4
搜索文字:主啊,爱
结果:记录#4、5、8
如果不对每个关键字进行循环,是否可以在 Spring Data JPA 中使用 LIKE 搜索(或其他方式)在一次调用中实现?
谢谢
我怀疑你有这样的实体
@Entity
public class Movie {
@Id
Long id;
String title;
//getters, setters ...
}
然后你需要存储库扩展JpaSpecificationExecutor
public interface MovieRepository
extends JpaRepository<Movie, Long>, JpaSpecificationExecutor<Movie> {
}
和Specification实用程序class
public class MovieSpecification {
public static Specification<Movie> titleContains(String searchWord) {
return (root, query, builder) -> {
Expression<String> titleLowerCase = builder.lower(root.get("title"));
return builder.like(titleLowerCase, "%" + searchWord.toLowerCase() + "%")
}
}
}
服务层的用法:
MovieRepository repository;
public List<Movie> getMoviesWhereTitleContainsAnyWord(List<String> words) {
if(words.isEmpty()) {
return Collections.emptyList();
}
Specification<Movie> specification = null;
for(String word : words) {
Specification<Movie> wordSpecification = MovieSpecification.titleContains(word);
if(specification == null) {
specification = wordSpecification;
} else {
specification = specification.or(wordSpecification);
}
}
return repository.findAll(specification);
}
您传递给该方法的单词越多,由于多个 or
子句,您的查询变得越慢。
如果您真的不想在一个循环中组合多个 LIKE
,您可以使用正则表达式。但是T-SQL中没有标准的RegEx匹配,但是有依赖数据库的解决方案
例如,在 Postgres 中,您可以使用 ~*
运算符将列与正则表达式
进行匹配
sql> select 'The Lord of the Rings: The Return of the King' ~* '(King|Queen|Princess|Lord|Duke|Baron)'
[2020-06-03 14:09:28] 1 row retrieved
当然查询前还要做一些准备工作
存储库层:
@Query(value = "select * from Movie_table where movie_title ~* :term", nativeQuery = true)
public List<Object[]> findMovies(@Param("term") String term);
服务层:
String searchText = "Lord, Love";
String searchTerm = Arrays.stream(searchText.split(","))
.map(String::trim)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining("|", "(", ")"));
List<Object[]> movies = movieRepository.findMovies(searchTerm);
// ... code to convert `Object[]`-s to `Movie` entities ...
P.S。与多个 LIKE/OR
查询
相比,此解决方案不太优雅
假设我有以下 table、
MOVIE_TITLE
- 蝙蝠侠前传
- 蝙蝠侠Returns
- Return 绝地
- 指环王: Return 之王
- 恋爱中的莎士比亚
- 死亡诗社
- 死侍
- 指环王:护戒使者
我想实现对电影标题的搜索并传递逗号分隔值(使用多个关键字搜索)
例如,
搜索文字:蝙蝠侠 结果:记录 #1,2
搜索文字:蝙蝠侠,Return 结果:记录 #1,2,3,4
搜索文字:主啊,爱 结果:记录#4、5、8
如果不对每个关键字进行循环,是否可以在 Spring Data JPA 中使用 LIKE 搜索(或其他方式)在一次调用中实现?
谢谢
我怀疑你有这样的实体
@Entity
public class Movie {
@Id
Long id;
String title;
//getters, setters ...
}
然后你需要存储库扩展JpaSpecificationExecutor
public interface MovieRepository
extends JpaRepository<Movie, Long>, JpaSpecificationExecutor<Movie> {
}
和Specification实用程序class
public class MovieSpecification {
public static Specification<Movie> titleContains(String searchWord) {
return (root, query, builder) -> {
Expression<String> titleLowerCase = builder.lower(root.get("title"));
return builder.like(titleLowerCase, "%" + searchWord.toLowerCase() + "%")
}
}
}
服务层的用法:
MovieRepository repository;
public List<Movie> getMoviesWhereTitleContainsAnyWord(List<String> words) {
if(words.isEmpty()) {
return Collections.emptyList();
}
Specification<Movie> specification = null;
for(String word : words) {
Specification<Movie> wordSpecification = MovieSpecification.titleContains(word);
if(specification == null) {
specification = wordSpecification;
} else {
specification = specification.or(wordSpecification);
}
}
return repository.findAll(specification);
}
您传递给该方法的单词越多,由于多个 or
子句,您的查询变得越慢。
如果您真的不想在一个循环中组合多个 LIKE
,您可以使用正则表达式。但是T-SQL中没有标准的RegEx匹配,但是有依赖数据库的解决方案
例如,在 Postgres 中,您可以使用 ~*
运算符将列与正则表达式
sql> select 'The Lord of the Rings: The Return of the King' ~* '(King|Queen|Princess|Lord|Duke|Baron)'
[2020-06-03 14:09:28] 1 row retrieved
当然查询前还要做一些准备工作
存储库层:
@Query(value = "select * from Movie_table where movie_title ~* :term", nativeQuery = true)
public List<Object[]> findMovies(@Param("term") String term);
服务层:
String searchText = "Lord, Love";
String searchTerm = Arrays.stream(searchText.split(","))
.map(String::trim)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining("|", "(", ")"));
List<Object[]> movies = movieRepository.findMovies(searchTerm);
// ... code to convert `Object[]`-s to `Movie` entities ...
P.S。与多个 LIKE/OR
查询