对 Single Table 的日期、整数和字符串数据类型字段执行多列搜索?
Perform multi column search on Date, Integer and String Data type fields of Single Table?
我在 Spring Data - Multi-column searches and 工作,我想搜索多个列,例如 Date
(Java 8 LocalDateTime
, Instant
, LocalDate
等,), Integer
和 String
数据类型。
但根据我的代码,仅考虑 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));
}
}
但是这种方法只考虑了String字段,没有考虑Date和Integer数据类型。我们该怎么做?
变化:
if(a.getJavaType().getSimpleName().equalsIgnoreCase("date"))
至:
if(a.getJavaType().getSimpleName().equalsIgnoreCase("LocalDate"))
分析问题,我给你一个更灵活的解决方案,这段代码解决了你的问题并允许生成动态查询on-the-fly。这个想法是使用任何类型的对象来进行查询。
首先,创建这个class来保存查询的参数
@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));
您可以聚合多个属性来进行搜索。我希望这段代码会有用。
此致
我在 Spring Data - Multi-column searches and Date
(Java 8 LocalDateTime
, Instant
, LocalDate
等,), Integer
和 String
数据类型。
但根据我的代码,仅考虑 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));
}
}
但是这种方法只考虑了String字段,没有考虑Date和Integer数据类型。我们该怎么做?
变化:
if(a.getJavaType().getSimpleName().equalsIgnoreCase("date"))
至:
if(a.getJavaType().getSimpleName().equalsIgnoreCase("LocalDate"))
分析问题,我给你一个更灵活的解决方案,这段代码解决了你的问题并允许生成动态查询on-the-fly。这个想法是使用任何类型的对象来进行查询。
首先,创建这个class来保存查询的参数
@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));
您可以聚合多个属性来进行搜索。我希望这段代码会有用。
此致