Perform multi column search on Date, Integer and String Data type fields of Single Table?

自闭症网瘾萝莉.ら 提交于 2020-01-24 00:20:11

问题


I am working Spring Data - Multi-column searches and Spring Data Jpa - The type Specifications<T> is deprecated where I wants to search for multiple columns like Date (Java 8 LocalDateTime, Instant, LocalDate etc.,), Integer and String data types.

But as per my code, only String fields are getting considered (as per logs in where clause)::

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 ?

Below is the code that I developed.

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;
}

Note: User can search for any value using on global search and whatever user search for, should be able to see data irrespective of data types.

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));
    }
}

But this approach only considering String fields and not Date and Integer data types. How can we do that ?


回答1:


Change:

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

to:

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



回答2:


Analyzing the problem, I give you a more flexible solution, this code resolves your problem and permits generate dynamic query on-the-fly. The idea is using whatever type object to make the query.

First, create this class to save the parameters of the query

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

Then build the specification based on the following code for each object

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;
}

}

Run the search in this way

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

You can aggregate many attributes to make the search. I Hope so this code will be useful.

Regards



来源:https://stackoverflow.com/questions/57134279/perform-multi-column-search-on-date-integer-and-string-data-type-fields-of-sing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!