Java lambda 动态查询

Java lambda 动态查询

该框架在大量数据 例如 1万条数据中,做筛选需要执行10秒。

提要

使用where表达式编写Java接口

问题描述

我在 微软 REST API指南 中 了解到, 微软设计数据查询是按照如下方式来获取数据的。

示例:返回所有价格低于 $10.00 的产品

GET https://api.contoso.com/v1.0/products?$filter=price lt 10.00
操作符号描述示例
比较符
eqEqualcity eq ‘Redmond’
neNot equalcity ne ‘London’
gtGreater thanprice gt 20
geGreater than or equalprice ge 10
ltLess thanprice lt 20
leLess than or equalprice le 100
逻辑符
andLogical andprice le 200 and price gt 3.5
orLogical orprice le 3.5 or price gt 200
notLogical negationnot price le 3.5
组符号
( )Precedence grouping(priority eq 1 or city eq ‘Redmond’) and price gt 100

示例:名称等于“牛奶”的所有产品

GET https://api.contoso.com/v1.0/products?$filter=name eq 'Milk' 

示例:名称不等于“牛奶”的所有产品

http GET https://api.contoso.com/v1.0/products?$filter=name ne 'Milk

示例:名称为“牛奶”且价格低于 2.55 的所有产品:

GET https://api.contoso.com/v1.0/products?$filter=name eq 'Milk' and price lt 2.55

示例:所有名称为“牛奶”或价格低于 2.55 的产品:

http GET https://api.contoso.com/v1.0/products?$filter=name eq 'Milk'  or price lt 2.55

示例 :所有名称为“牛奶”或“鸡蛋”且价格低于 2.55 的产品:

http GET https://api.contoso.com/v1.0/products?$filter=(name eq 'Milk' or name eq 'Eggs') and price lt 2.55 

我在MSDN了解到
假设你有多个实体类型:

record Person(string LastName, string FirstName, DateTime DateOfBirth);
record Car(string Model, int Year);

对于这些实体类型中的任何一个,你都需要筛选并仅返回那些在其某个 string 字段内具有给定文本的实体。 对于 Person,你希望搜索 FirstName 和 LastName 属性:

string term = /* ... */;
var personsQry = new List<Person>()
    .AsQueryable()
    .Where(x => x.FirstName.Contains(term) || x.LastName.Contains(term));

但对于 Car,你希望仅搜索 Model 属性:

string term = /* ... */;
var carsQry = new List<Car>()
    .AsQueryable()
    .Where(x => x.Model.Contains(term));

尽管可以为 IQueryable 编写一个自定义函数,并为 IQueryable 编写另一个自定义函数。

关于C#的相关文档,您可以移步

C# 动态Linq 建立模糊查询通用工具类

但今天的重点是Java.

适用场景

先决条件

java 8

由于该方法使用反射处理查询lambda表达式,所以检索效率肯定没有使用JDBC连接数据库快。

我相信应该没有 后端工程师 直接把十万条数据一次性的交给 前端工程师 吧。

准备工作

maven库

		<dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.4</version>
        </dependency>

准备数据集

实体类

@Data
public class Product extends Model {
    public Product(String name, BigDecimal price, Float fPrice, Double dPrice, Integer number) {
        this.name = name;
        this.price = price;
        this.fPrice = fPrice;
        this.dPrice = dPrice;
        this.number = number;

    }
    private String name;
    private BigDecimal price;
    private Float fPrice;
    private Double dPrice;
    private Integer number;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Float getfPrice() {
        return fPrice;
    }

    public void setfPrice(Float fPrice) {
        this.fPrice = fPrice;
    }

    public void setdPrice(Double dPrice) {
        this.dPrice = dPrice;
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public Double getdPrice() {
        return dPrice;
    }
}

模拟获取方法

public List<Product> getProduct(){
        List<Product> list = new ArrayList<>();

            list.add(new Product("水壶", BigDecimal.valueOf(8D), 5F, 9D, 3) {
            });
            list.add(new Product("电冰箱", BigDecimal.valueOf(1D), 4F, 10D, 3) {
            });
            list.add(new Product("空调", BigDecimal.valueOf(3D), 3F, 7D, 11) {
            });
            list.add(new Product("电热毯", BigDecimal.valueOf(4D), 2F, 8D, 77) {
            });
            list.add(new Product("暖气片", BigDecimal.valueOf(5D), 1F, 5D, 8) {
            });
        return list;
    }

编写lambda表达式

		List<Product> products =  getProduct();
        Predicate<Product> isBottle = std -> "水壶".equals(std.getName());
        products.stream().filter(isBottle).collect(Collectors.toList());

细心的你一定能发现,这里的表达式可以乱写

products.stream().filter(isBottle.or(isBottle)).collect(Collectors.toList());

搭建框架

查询参数类

@Data
public class FilterParam {
    private String field;
    private String value;
}

封装表达式函数


public class FilterUtils<T> {
    // 转发类
    public List<T> filter(Predicate<T> predicate, List<T> list) {
        return list.stream().filter(predicate).collect(Collectors.toList());
    }
    public Predicate<T> containsFilter(FilterParam param){
            return std -> {
                try {
                    return BeanUtils.getProperty(std,param.getField()).contains(param.getValue());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
                return true;
            };
    }
    public Predicate<T> equalFilter(FilterParam param){
        return std -> {
            try {
                return BeanUtils.getProperty(std,param.getField()).equals(param.getValue());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return true;
        };
    }
    public Predicate<T> notEqualFilter(FilterParam param){
        return std -> {
            try {
                return !BeanUtils.getProperty(std,param.getField()).equals(param.getValue());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return true;
        };
    }
    public Predicate<T> greatThanFilter(FilterParam param){
        return std -> {
            try {
                BigDecimal field = new BigDecimal(BeanUtils.getProperty(std, param.getField()));
                BigDecimal value = new BigDecimal(param.getValue());
                return field.compareTo(value) == 1 ;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return true;
        };
    }
    public Predicate<T> greatThanOrEqualFilter(FilterParam param){
        return std -> {
            try {
                BigDecimal field = new BigDecimal(BeanUtils.getProperty(std, param.getField()));
                BigDecimal value = new BigDecimal(param.getValue());
                return field.compareTo(value) == 1 || field.compareTo(value) == 0 ;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return true;
        };
    }
    public Predicate<T> lessThanFilter(FilterParam param){
        return std -> {
            try {
                BigDecimal field = new BigDecimal(BeanUtils.getProperty(std, param.getField()));
                BigDecimal value = new BigDecimal(param.getValue());
                return field.compareTo(value) == -1 ;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return true;
        };
    }
    public Predicate<T> lessThanOrEqualFilter(FilterParam param){
        return std -> {
            try {
                BigDecimal field = new BigDecimal(BeanUtils.getProperty(std, param.getField()));
                BigDecimal value = new BigDecimal(param.getValue());
                return field.compareTo(value) == -1 ||  field.compareTo(value) == 0;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return true;
        };
    }
}

如何动态描述where语句?

name eq '暖气片' | ((name eq '水壶' & name eq '电冰箱') | name eq '电热毯') | name eq '水壶'

数据结构-栈

栈

来点代码

public class QueryUtils<T> {

    private static Logger logger =
            LoggerFactory.getLogger(QueryUtils.class);
    FilterUtils<T> filterUtils = new FilterUtils<>();
    public List<T> getQuery(List<T> list,String queryParam){
        // 表达式 栈
        Stack<Predicate<T>> expressions = new Stack<Predicate<T>>();
        // 运算符 栈
        Stack<Character> stack = new Stack<>();
       
        StringBuilder express = new StringBuilder();
        queryParam =  queryParam.replaceAll(" and "," & ").replaceAll(" or "," | ");

        StringBuilder query = new StringBuilder(queryParam);
        do{
            // 如果字符头部有 (
            if(query.charAt(0)=='('){
                stack.push('(');
                query.deleteCharAt(0);
            }
            // 否则 读取字符串
            else{
                // 如果读取到 | 或者 &
                // 那么 把运算符 和 表达式 如栈
                // 遇到运算符 |
                if(query.charAt(0) == '|'){
                    stack.push('|');
                    // 如果非空格,才入栈
                    if(express.toString().trim().length()!=0){
                        expressions.push(getPredicate(express.toString()));
                    }
                    // 清空当前缓存的表达式
                    express.delete(0,express.length());
                }
                // 遇到运算符 &
                else if(query.charAt(0) == '&'){
                    stack.push('&');
                    // 如果非空格,才入栈
                    if(express.toString().trim().length()!=0){
                        expressions.push(getPredicate(express.toString()));
                    }
                    // 清空当前缓存的表达式
                    express.delete(0,express.length());
                }
                // 遇到运算符 )
                else if(query.charAt(0) == ')'){
                    String end = express.toString();
                    Predicate<T> y = getPredicate(end);
                    // 清空当前缓存的表达式
                    express.delete(0,express.length());
                    // 遇到尾部,我要去找当前 ) 对应的 (的组
                    Character _opt = null;
                    // peek 不弹出对象 只拿 栈顶
                    // 获取当前 括号下的 所有 | & 符号
                    // 当栈顶不是 (时
                    while(stack.peek() != '('){
                       Character _operator = stack.pop();
                       Predicate<T>  x =  expressions.pop();
                       // 存储表达式
                       if(_operator=='|'){
                           y = y.or(x);
                       }else if(_operator=='&'){
                           y = y.and(x);
                       }
                    }
                    // 把(括号去掉
                    stack.pop();
                    expressions.push(y);

                }
                // 读取表达式
                else {
                    express.append(query.charAt(0));
                }
                query.deleteCharAt(0);
                //  | a eq b 这样的数据不会被 上面的if语句扫描到
                //  需要通过该方法 将末尾的 表达式 加入栈中
                if(query.length()==0){
                    if(express.toString().trim().length()!=0){
                        expressions.push(getPredicate(express.toString()));
                    }
                }
            }
        }while (query.length()!=0);
        // 获取栈顶
        Predicate<T> y =  expressions.pop();
        while(!stack.empty()){
            Character _operator = stack.pop();
            Predicate<T>  x = expressions.pop();
            // 存储表达式
            if(_operator=='|'){
                y = y.or(x);
            }else if(_operator=='&'){
                y = y.and(x);
            }
        }
        list = filterUtils.filter(y, list);
        return list;
    }
    private Predicate<T> getPredicate(String expression){
        Predicate<T> item = null;
        if(expression.contains("eq")){
            String[] params = expression.split("eq");
            FilterParam param = new FilterParam();
            param.setField(params[0].replaceAll("'","").trim());
            param.setValue(params[1].replaceAll("'","").trim());
            item = filterUtils.equalFilter(param);
        }else if(expression.contains("ne")){
            String[] params = expression.split("ne");
            FilterParam param = new FilterParam();
            param.setField(params[0].replaceAll("'","").trim());
            param.setValue(params[1].replaceAll("'","").trim());
            item = filterUtils.notEqualFilter(param);
        }else if(expression.contains("gt")){
            String[] params = expression.split("gt");
            FilterParam param = new FilterParam();
            param.setField(params[0].replaceAll("'","").trim());
            param.setValue(params[1].replaceAll("'","").trim());
            item = filterUtils.greatThanFilter(param);
        }else if(expression.contains("ge")){
            String[] params = expression.split("ge");
            FilterParam param = new FilterParam();
            param.setField(params[0].replaceAll("'","").trim());
            param.setValue(params[1].replaceAll("'","").trim());
            item = filterUtils.greatThanOrEqualFilter(param);
        }else if(expression.contains("lt")){
            String[] params = expression.split("lt");
            FilterParam param = new FilterParam();
            param.setField(params[0].replaceAll("'","").trim());
            param.setValue(params[1].replaceAll("'","").trim());
            item = filterUtils.lessThanFilter(param);
        }else if(expression.contains("le")){
            String[] params = expression.split("le");
            FilterParam param = new FilterParam();
            param.setField(params[0].replaceAll("'","").trim());
            param.setValue(params[1].replaceAll("'","").trim());
            item = filterUtils.lessThanOrEqualFilter(param);
        }else if(expression.contains("ct")){
            String[] params = expression.split("ct");
            FilterParam param = new FilterParam();
            param.setField(params[0].replaceAll("'","").trim());
            param.setValue(params[1].replaceAll("'","").trim());
            item = filterUtils.containsFilter(param);
        }
        return item;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bosaidongmomo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值