Java lambda 动态查询
该框架在大量数据 例如 1万条数据中,做筛选需要执行10秒。
提要
问题描述
我在 微软 REST API指南 中 了解到, 微软设计数据查询是按照如下方式来获取数据的。
示例:返回所有价格低于 $10.00 的产品
GET https://api.contoso.com/v1.0/products?$filter=price lt 10.00
操作符号 | 描述 | 示例 |
---|---|---|
比较符 | ||
eq | Equal | city eq ‘Redmond’ |
ne | Not equal | city ne ‘London’ |
gt | Greater than | price gt 20 |
ge | Greater than or equal | price ge 10 |
lt | Less than | price lt 20 |
le | Less than or equal | price le 100 |
逻辑符 | ||
and | Logical and | price le 200 and price gt 3.5 |
or | Logical or | price le 3.5 or price gt 200 |
not | Logical negation | not 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#的相关文档,您可以移步
但今天的重点是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;
}
}