【问题标题】:Perform multi column search on Date, Integer and String Data type fields of Single Table?对单表的日期、整数和字符串数据类型字段执行多列搜索?
【发布时间】:2019-07-21 14:51:01
【问题描述】:

我在 Spring Data - Multi-column searchesSpring Data Jpa - The type Specifications<T> is deprecated 工作,我想搜索多个列,例如 Date(Java 8 LocalDateTimeInstantLocalDate 等)、Integer 和 @987654328 @数据类型。

但根据我的代码,只有String 字段被考虑(根据where 子句中的日志)::

select
    employee0_.employee_id as employee1_0_,
    employee0_.birth_date as birth_da2_0_,
    employee0_.email_id as email_id3_0_,
    employee0_.first_name as first_na4_0_,
    employee0_.last_name as last_nam5_0_,
    employee0_.project_association as project_6_0_,
    employee0_.status as status7_0_ 
from
    employee employee0_ 
where
    employee0_.first_name like ? 
    or employee0_.email_id like ? 
    or employee0_.status like ? 
    or employee0_.last_name like ?

下面是我开发的代码。

Employee.java

@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Employee implements Serializable{
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="EMPLOYEE_ID")
    private Long employeeId;

    @Column(name="FIRST_NAME")
    private String firstName;

    @Column(name="LAST_NAME")
    private String lastName;

    @Column(name="EMAIL_ID")
    private String email;

    @Column(name="STATUS")
    private String status;

    @Column(name="BIRTH_DATE")
    private LocalDate birthDate;

    @Column(name="PROJECT_ASSOCIATION")
    private Integer projectAssociation;
}

注意:用户可以使用全局搜索来搜索任何值,无论用户搜索什么,都应该能够看到数据,而不管数据类型如何。

EmployeeSpecification.java

public class EmployeeSpecification {

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

        return (root, query, builder) -> builder
                .or(root.getModel().getDeclaredSingularAttributes().stream().filter(a -> {
                    if (a.getJavaType().getSimpleName().equalsIgnoreCase("String")) {
                        return true;
                    }else if(a.getJavaType().getSimpleName().equalsIgnoreCase("date")) {
                        return true;
                    }
                    else {
                        return false;
                    }
                }).map(a -> builder.like(root.get(a.getName()), finalText)).toArray(Predicate[]::new));
    }
}

但这种方法只考虑字符串字段而不考虑日期和整数数据类型。我们该怎么做?

【问题讨论】:

    标签: java spring-data-jpa jpa-2.2


    【解决方案1】:

    变化:

    if(a.getJavaType().getSimpleName().equalsIgnoreCase("date")) 
    

    到:

    if(a.getJavaType().getSimpleName().equalsIgnoreCase("LocalDate"))
    

    【讨论】:

    • 日志显示 LocalDate 已经出现在 where 子句中
    • 我收到java.lang.IllegalArgumentException: Parameter value [%2019-07-21%] did not match expected type [java.time.LocalDate (n/a)]
    • 当然,hibernate尝试验证数据类型并找到一个字符串用于与LocalDate进行比较,这就是错误。您可以设置一个列表参数,指明您要搜索的每个参数。 baeldung.com/…docs.spring.io/spring-data/jpa/docs/2.1.9.RELEASE/reference/… 中存在有趣的注释(Capitule 5.5)
    【解决方案2】:

    分析问题,我给你一个更灵活的解决方案,这段代码解决了你的问题,并允许动态生成动态查询。这个想法是使用任何类型的对象来进行查询。

    首先,创建这个类来保存查询的参数

    @RequiredArgsConstructor
    public class Search {
        @Getter
        @NonNull private String field;
        @Getter
        @NonNull private String operator;
        @Getter
        @NonNull private Object value;
    }
    

    然后根据以下代码为每个对象构建规范

    public static Specification<Employee> searchFreeAttrsEmployee(List<Search> attributes) {
        return new Specification<Employee>() {
            private static final long serialVersionUID = 4817323527595445596L;
            public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> r=new ArrayList<Predicate>(); 
                attributes.stream().forEach(t -> {
                    r.add(setParams(t, root, builder));
                });
                Predicate list[] = new Predicate[r.size()];
                r.toArray(list);
                return builder.or(list);
            }
        };
    }
    
    public static Predicate setParams(Search t, Root<?> root, CriteriaBuilder builder) {
        if (t.getOperator().equals("like")) {
            return builder.like(root.get(t.getField()), t.getValue().toString());
        }
        if (t.getOperator().equals("equal")) {
            return builder.equal(root.get(t.getField()), t.getValue().toString());
        }       
        if (t.getOperator().equals("gtInt")) {
            return builder.gt(root.get(t.getField()), Integer.valueOf(t.getValue().toString()));
        }       
        if (t.getOperator().equals("eqInt")) {
            return builder.equal(root.get(t.getField()), Integer.valueOf(t.getValue().toString()));
        }       
    
        return null;
    }
    

    }

    以这种方式运行搜索

        List<Search> l=new ArrayList<Search>();
        l.add(new Search("firstName","like","Peter"));
        List<Employee> list = pe.findAll(Specifications.searchFreeAttrsEmployee(l));
    

    您可以聚合许多属性来进行搜索。我希望这段代码会有用。

    问候

    【讨论】:

    • 谢谢,我会测试这个解决方案并让你知道。同时你会在stackoverflow.com/questions/57167639/…上指导我吗?
    • 注意:在类中将属性 date_birth 更改为:@JsonFormat(pattern = "dd/MM/yyyy") @JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) @Column(name = "BIRTH_DATE") private LocalDatebirthDate;
    猜你喜欢
    • 2015-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-14
    • 2018-09-26
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多