• Java lambda 动态查询


    Java lambda 动态查询

    提要

    使用where表达式编写Java接口

    问题描述

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

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

    GET https://api.contoso.com/v1.0/products?$filter=price lt 10.00
    
    • 1
    操作符号描述示例
    比较符
    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' 
    
    • 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    但今天的重点是Java.

    适用场景

    先决条件

    java 8

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

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

    准备工作

    maven库

    		<dependency>
                <groupId>commons-beanutilsgroupId>
                <artifactId>commons-beanutilsartifactId>
                <version>1.9.4version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    准备数据集

    实体类

    @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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    模拟获取方法

    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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    编写lambda表达式

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

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

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

    搭建框架

    查询参数类

    @Data
    public class FilterParam {
        private String field;
        private String value;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    封装表达式函数

    
    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;
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    如何动态描述where语句?

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

    数据结构-栈

    栈

    来点代码

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
  • 相关阅读:
    测试大老都是怎么理解cookie&session的?
    图像分割:LR-ASPP模型介绍
    leetcode 135. 分发糖果
    基于微信小程序的警局报案便民服务平台#毕业设计
    【TypeScript】什么是字面量类型、类型推断、类型拓宽和类型缩小?
    Mathorcup数学建模竞赛第四届-【妈妈杯】C题:家庭暑假旅游套餐的设计(附MATLAB代码)
    Rust中Option、Result的map和and_then的区别
    Pinia 及其数据持久化 Vue新一代状态管理插件
    周鸿祎“中途撤场”哪吒汽车,对造车失去信心,还是另有他途?
    科技资讯|微软获得AI双肩包专利,Find My防丢背包大火
  • 原文地址:https://blog.csdn.net/bosaidongmomo/article/details/126154688