package com.izouma.nineth.utils; import com.izouma.nineth.annotations.Searchable; import com.izouma.nineth.dto.PageQuery; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.lang.reflect.Field; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; @Slf4j @SuppressWarnings("ALL") public class JpaUtils { public static PageRequest toPageRequest(PageQuery pageQuery) { PageRequest pageRequest; if (StringUtils.isNotEmpty(pageQuery.getSort())) { List orders = new ArrayList<>(); for (String sortStr : pageQuery.getSort().split(";")) { String direction = "asc"; String prop = sortStr; if (sortStr.contains(",asc") || sortStr.contains(",desc")) { prop = sortStr.split(",")[0]; direction = sortStr.split(",")[1]; } orders.add("asc".equals(direction) ? Sort.Order.asc(prop) : Sort.Order.desc(prop)); } pageRequest = PageRequest.of(pageQuery.getPage(), pageQuery.getSize(), Sort.by(orders)); } else { pageRequest = PageRequest.of(pageQuery.getPage(), pageQuery.getSize()); } return pageRequest; } public static Specification toSpecification(PageQuery pageQuery, Class queryClass) { return (Specification) (root, criteriaQuery, criteriaBuilder) -> { List and = toPredicates(pageQuery, queryClass, root, criteriaQuery, criteriaBuilder); return criteriaBuilder.and(and.toArray(new Predicate[0])); }; } public static List toPredicates(PageQuery pageQuery, Class queryClass, Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) { List and = new ArrayList<>(); pageQuery.getQuery().forEach((property, value) -> { if (value == null) { return; } if (String.class.equals(value.getClass())) { if (StringUtils.isEmpty((String) value)) { return; } } Field field = getDeclaredField(queryClass, property); if (field == null) return; Class fieldType = field.getType(); if (Enum.class.isAssignableFrom(fieldType)) { if (value instanceof Collection) { if (!((Collection) value).isEmpty()) { List list = new ArrayList(); for (Object o : ((Collection) value)) { list.add(Enum.valueOf(fieldType, String.valueOf(o))); } and.add(root.get(property).in(list)); } } else if (value instanceof String && StringUtils.isNotEmpty((String) value)) { if (((String) value).contains(",")) { String[] arr = ((String) value).split(","); List list = new ArrayList(); for (String s : arr) { list.add(Enum.valueOf(fieldType, s)); } and.add(root.get(property).in(list)); } else { and.add(criteriaBuilder.and(criteriaBuilder .equal(root.get(property), Enum.valueOf(fieldType, String.valueOf(value))))); } } } else if (LocalDateTime.class == fieldType) { if (value instanceof List) { List list = (List) value; if (list.size() == 1) { LocalDateTime start = DateTimeUtils .toLocalDateTime((String) list.get(0), "yyyy-MM-dd HH:mm:ss"); and.add(criteriaBuilder.greaterThanOrEqualTo(root.get(property), start)); } else if (list.size() == 2) { LocalDateTime end = DateTimeUtils .toLocalDateTime((String) list.get(1), "yyyy-MM-dd HH:mm:ss"); and.add(criteriaBuilder.lessThanOrEqualTo(root.get(property), end)); } } else if (value instanceof String && Pattern .matches("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$", (String) value)) { String[] arr = ((String) value).split(","); LocalDateTime start = DateTimeUtils.toLocalDateTime(arr[0], "yyyy-MM-dd HH:mm:ss"); and.add(criteriaBuilder.greaterThanOrEqualTo(root.get(property), start)); LocalDateTime end = DateTimeUtils.toLocalDateTime(arr[1], "yyyy-MM-dd HH:mm:ss"); and.add(criteriaBuilder.lessThanOrEqualTo(root.get(property), end)); } else { and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get(property), DateTimeUtils .toLocalDateTime((String) value, "yyyy-MM-dd HH:mm:ss")))); } } else if (LocalDate.class == fieldType) { if (value instanceof List) { List list = (List) value; if (list.size() == 1) { LocalDate start = DateTimeUtils .toLocalDate((String) list.get(0), "yyyy-MM-dd"); and.add(criteriaBuilder.greaterThanOrEqualTo(root.get(property), start)); } else if (list.size() == 2) { LocalDate end = DateTimeUtils .toLocalDate((String) list.get(1), "yyyy-MM-dd"); and.add(criteriaBuilder.lessThanOrEqualTo(root.get(property), end)); } } else if (value instanceof String && Pattern .matches("^\\d{4}-\\d{2}-\\d{2},\\d{4}-\\d{2}-\\d{2}$", (String) value)) { String[] arr = ((String) value).split(","); LocalDate start = DateTimeUtils.toLocalDate(arr[0], "yyyy-MM-dd"); and.add(criteriaBuilder.greaterThanOrEqualTo(root.get(property), start)); LocalDate end = DateTimeUtils.toLocalDate(arr[1], "yyyy-MM-dd"); and.add(criteriaBuilder.lessThanOrEqualTo(root.get(property), end)); } else { and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get(property), DateTimeUtils .toLocalDateTime((String) value, "yyyy-MM-dd")))); } } else { and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get(property), value))); } }); if (StringUtils.isNotEmpty(pageQuery.getSearch())) { Field[] fields = queryClass.getDeclaredFields(); List or = new ArrayList<>(); try { if (StringUtils.isNumeric(pageQuery.getSearch())) { or.add(criteriaBuilder.equal(root.get("id"), Long.parseLong(pageQuery.getSearch()))); } } catch (Exception ignored) { } for (Field field : fields) { Searchable annotation = field.getAnnotation(Searchable.class); if (annotation == null) { continue; } if (!annotation.value()) { continue; } if (field.getType() == String.class) { or.add(criteriaBuilder.like(root.get(field.getName()), "%" + pageQuery.getSearch() + "%")); } else if (field.getType() == Long.class || field.getType() == long.class) { try { or.add(criteriaBuilder.equal(root.get(field.getName()), Long.parseLong(pageQuery.getSearch()))); } catch (Exception ignore) { } } else if (field.getType() == Integer.class || field.getType() == int.class) { try { or.add(criteriaBuilder .equal(root.get(field.getName()), Integer.parseInt(pageQuery.getSearch()))); } catch (Exception ignore) { } } } and.add(criteriaBuilder.or(or.toArray(new Predicate[0]))); } return and; } private static Field getDeclaredField(Class clazz, String property) { String className = clazz.getName(); while (clazz != null && clazz != Object.class) { try { return clazz.getDeclaredField(property); } catch (NoSuchFieldException ignored) { } clazz = clazz.getSuperclass(); } log.error("no such field [{}] in class [{}]", property, className); return null; } }