当前位置: 首页 > 知识库问答 >
问题:

Spring Data-多列搜索

长孙弘壮
2023-03-14

我正在使用Spring数据进行分页和排序。但是,我想执行多列搜索。

现在,我在我的存储库接口中使用注释@Query,如下所示:

public interface MyRepository extends PagingAndSortingRepository<Item,Long> {

    @Query(value="select mt from MY_TABLE mt where mt.field1 = %searchtext% or mt.field2 = %searchtext% or mt.field3 = %searchtext%")    
    Page<Item> findByAllColumns(@Param("searchtext") String searchtext, Pageable pageable);

}

我想知道是否有另一种方法可以做到,因为表中的列数可能很高。

谢谢你的帮助。

edit(在Brandon Oakley的评论之后澄清问题):这个解决方案的问题在于@Query注释的where子句,因为我们必须为我们想要搜索的每一列重复完全相同的searchtext参数

共有3个答案

吕冠宇
2023-03-14

结合前面的两个答案:如果您不想将API和数据库模式结合起来,或者换句话说,您不想让用户提供字符串列名,您可以过滤掉那些不是字符串的属性,并将< code>like应用于所有不是字符串的属性。在下面的示例中,它将尝试在列的值中搜索< code > text :< code > name 、< code>field1 、< code>field2和< code>field3。

实体示例:

@Entity
public class MyEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public int id;
    public String name;
    public String field2;
    public String field3;
    public String field4;
}

规格示例:

public class EntitySpecification {

    public static Specification<MyEntity> textInAllColumns(String text) {

        if (!text.contains("%")) {
            text = "%"+text+"%";
        }
        final String finalText = text;

        return new Specification<MyEntity>() {
            @Override
            public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> cq, CriteriaBuilder builder) {
                return builder.or(root.getModel().getDeclaredSingularAttributes().stream().filter(a-> {
                    if (a.getJavaType().getSimpleName().equalsIgnoreCase("string")) {
                        return true;
                    }
                    else {
                        return false;
                }}).map(a -> builder.like(root.get(a.getName()), finalText)
                    ).toArray(Predicate[]::new)
                );
            }
        };
    }

 }

存储库示例:

public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, Integer> {
    List<MyEntity> findAll(Specification<MyEntity> spec);
}

使用示例:

List<MyEntity> res = failureRepository.findAll(Specifications.where(FailureSpecification.textInAllColumns(text)));

另一个更新(在所有类型的列中搜索,并使用lambdas将字段列入白名单 - 不检查代码)

public class EmployeeSpecification {
    public static Specification<Employee> textInAllColumns(String text, Set<String> fields) {
        if (!text.contains("%")) {
            text = "%" + text + "%";
        }
        final String finalText = text;

        return  (Specification<Employee>) (root, query, builder) -> 
                builder.or(root.getModel().getDeclaredSingularAttributes().stream().filter(a -> {
                return fields.contains(a.getName());
            }).map(a -> builder.like(root.get(a.getName()), finalText)).toArray(Predicate[]::new));
    }
} 
龙华翰
2023-03-14

您可以使用规格。这也为您提供了更大的灵活性。您可以有一个方法,但对一个查询使用多个规范:

Page<Item> findAll(Specification<T> spec, Pageable pageable);

myRepository.findAll(textInAllColumns(searchText), pageable);
锺离旻
2023-03-14

以下是该用户规范的示例:

public static Specification<User> containsTextInName(String text) {
    if (!text.contains("%")) {
        text = "%" + text + "%";
    }
    String finalText = text;
    return (root, query, builder) -> builder.or(
            builder.like(root.get("lastname"), finalText),
            builder.like(root.get("firstname"), finalText)
    );
}

或更可定制的实施:

public static Specification<User> containsTextInAttributes(String text, List<String> attributes) {
    if (!text.contains("%")) {
        text = "%" + text + "%";
    }
    String finalText = text;
    return (root, query, builder) -> builder.or(root.getModel().getDeclaredSingularAttributes().stream()
            .filter(a -> attributes.contains(a.getName()))
            .map(a -> builder.like(root.get(a.getName()), finalText))
            .toArray(Predicate[]::new)
    );
}

public static Specification<User> containsTextInName(String text) {
    return containsTextInAttributes(text, Arrays.asList("lastname", "firstname"));
}

用法:

userRepository.findAll(Specifications.where(UserSpecifications.containsTextInName("irs")))
 类似资料:
  • 问题内容: 我正在使用Spring Data进行分页和排序。但是,我想执行多列搜索。 现在,我在存储库界面中使用注释 @Query ,如下所示: 编辑: 此解决方案中的问题在于@Query注释的where子句,因为我们必须对要搜索的每一列重复完全相同的searchtext参数 (在Brandon Oakley的评论后澄清问题) 我想知道是否还有另一种方法,因为表中的列数可能很高。 谢谢你的帮助。

  • 我与MongoDB和Spring数据的聚合框架进行了相当长的一段时间的斗争,我实际上想知道我想做的事情是否真的可能。 null 我试图从这种方法开始,但我找不到如何在一个聚合pipeline中完成所有事情的方法:

  • 问题背景 之前做springboot项目在操作数据库方面一直在使用的是Mybatis,最近在查阅资料的时候接触到了SpringData JPA与SpringData JDBC,想问一下大佬们,这三个框架如何选型

  • 问题内容: 我正在尝试提供一种搜索功能,该功能将搜索多列以查找基于关键字的匹配项。该查询: 仅适用于搜索一列,我注意到用逗号分隔列名称会导致错误。那么可以在mysql中搜索多个列吗? 问题答案: 您可以使用AND或OR运算符,具体取决于要返回的搜索内容。 这两个子句必须匹配才能返回记录。或者: 如果任一子句匹配,则将返回记录。 有关使用MySQL SELECT查询可以执行的操作的更多信息,请尝试使

  • 我是Neo4j和SDN的新手。我试图使用@index(unique=true)为我的实体创建主键。它创建唯一的节点,但如果使用索引的相同属性值,它将替换其他属性。如: 但是,我想抛出一个关于主键冲突/重复节点的异常。 有什么方法可以达到同样的效果吗?

  • 我想在Spring-Boot后端创建一个多字段搜索。如何使用实现这一点? 环境 前端的UI是Jquery DataTable。每列允许应用单个字符串搜索项。跨多个列的搜索词由联接。 看来QueryDSL是解决这个问题的更简单、更好的方法。我在用Gradle。我需要改变我的构造吗?