英文:
Spring Data - Multi-column search by only one value
问题
我正在使用Spring Data将一系列客户添加到我的网格中。
在我的存储库中,我有以下方法:
public List<CustomerDto> findAllByFirstNameContainingOrLastNameContainingAllIgnoreCase(String firstName, String lastName);
它可以工作,但实际上,firstName
和 lastName
参数具有相同的值。
是否有可能仅通过使用方法关键字功能(我不想通过使用 @Query 注解并编写自己的查询来增加复杂性)来实现只有一个参数来过滤我的两个列,并避免我在方法中提供相同的参数,就像是:
repository.myNewAwesomeMethodForFilteringTwoColumnsWithOneValue(filterValue);
而不是:
repository.findAllByFirstNameContainingOrLastNameContainingAllIgnoreCase(filterValue, filterValue);
如果您有任何想法或建议,谢谢您的帮助!
英文:
I am using Spring Data for adding into my grid a list of Customer.
In my repository, I have the following method :
public List<CustomerDto> findAllByFirstNameContainingOrLastNameContainingAllIgnoreCase(String firstName, String lastName);
It works, but in reality, firstName
and lastName
parameters are the same value.
Is there by any chance the possibility to achieve, by only using the method's keyword functionnality (I don't wanna add complexity by using the @Query annotation and writing my own query), to have only one parameter for filtering my two columns and avoiding me to provide the same parameter twice in the method like :
repository.myNewAwesomeMethodForFilteringTwoColumnsWithOneValue(filterValue);
Instead of :
repository.findAllByFirstNameContainingOrLastNameContainingAllIgnoreCase(filterValue, filterValue);
Thanks for your help, if you have any ideas or suggestions !
答案1
得分: 1
一个通用的解决方案是使用JPA规范(Specification)。虽然这可能是一个过度解决方案,但如果您有许多针对多列搜索的用例,我认为这是值得的。
我首先演示一下我是如何使用它的。
List<String> bookColumns = Arrays.asList("title", "genre", "author.name");
// 搜索"Spring":匹配2本书 - 《Spring in Action》和《Spring Integration》
GenericSearchSpecification<Book> searchSpecification = new GenericSearchSpecification<>(bookColumns, "Spring");
List<Book> books = bookRepository.findAll(searchSpecification);
Assert.assertEquals(2, books.size());
GenericSearchSpecification是我创建的一个类,它有一个接受两个参数的构造函数:
- searchableColumns
- searchString
现在是类的实现部分
public class GenericSearchSpecification<T> implements Specification<T> {
private List<String> columns;
private String search;
public GenericSearchSpecification(List<String> columns, String search) {
this.columns = columns;
this.search = search;
}
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
for (String column : columns) {
Path<?> path = buildPath(column, root);
Predicate predicate = criteriaBuilder.like(criteriaBuilder.upper(path), "%" + search.toUpperCase() + "%");
predicates.add(predicate);
}
return criteriaBuilder.or(predicates.stream().toArray(Predicate[]::new));
}
private Path<?> buildPath(String column, Path<?> path) {
if (!column.contains(".")) return path.get(column);
String[] parts = column.split("\\.");
for (String part : parts) {
path = path.get(part);
}
return path;
}
}
如果您感兴趣,欢迎访问我专门为此创建的文章链接:https://medium.com/javarevisited/jpa-specification-a-generic-search-e8695b1d19ec
英文:
A generic solution that I can think of is using JPA Specification. This might be an overkill solution but if you have a lot of use cases for multi-column search then I think it's worth it.
I'll demonstrate it first how I use it.
List<String> bookColumns = Arrays.asList("title", "genre", "author.name");
// Search "Spring": matches 2 Books - Spring in Action and Spring Integration
GenericSearchSpecification<Book> searchSpecification = new GenericSearchSpecification<>(bookColumns, "Spring");
List<Book> books = bookRepository.findAll(searchSpecification);
Assert.assertEquals(2, books.size());
GenericSearchSpecification is a class that I've created which has a constructor accepting 2 parameters
- searchableColumns
- searchString
Now for the class implementation
public class GenericSearchSpecification<T> implements Specification<T> {
private List<String> columns;
private String search;
public GenericSearchSpecification(List<String> columns, String search) {
this.columns = columns;
this.search = search;
}
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
for (String column: columns) {
Path path = buildPath(column, root);
Predicate predicate = criteriaBuilder.like(criteriaBuilder.upper(path), "%" + search.toUpperCase() + "%");
predicates.add(predicate);
}
return criteriaBuilder.or(predicates.stream().toArray(Predicate[]::new));
}
private Path buildPath(String column, Path path) {
if (!column.contains(".")) return path.get(column);
String[] parts = column.split("\\.");
for (String part: parts) {
path = path.get(part);
}
return path;
}
}
If you're interested feel free to visit the article that I created specifically for this: https://medium.com/javarevisited/jpa-specification-a-generic-search-e8695b1d19ec
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论