设计模式 + java8方法引用 实现任意表的过滤器
会用到下面2个依赖,原因是在今天的案例中,我想在我代码中使用上Entity::getFieldName
这种形式
LambdaQueryWrapper<ApplicationDashboard> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ApplicationDashboard::getAppCode, "Code1");// 使用这种形式的编码
之所以喜欢这种形式是因为修改属性字段名称时直接用Idea的重命名修改,这样我们不需要去自己去其他使用到的地方一个个改。
举个例子来说,AppCode 重命名为Code
那么IDEA重命名后,相应的get
方法也会自己修改好
queryWrapper.eq(ApplicationDashboard::getCode, "Code1");
用到的依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
场景
我希望生成这样一条查询sql,其中application_dashboard
表只是一个例子,需要实现任意表的 sql 查询
select * from application_dashboard
where
(app_code='AppCode1' or app_code='AppCode2' )
and app_context is like '%dev%'
and type <> '3'
因为一些原因,我们需要返回下面的filter字符串,大致上和上面的where condition类似
// 生成filter:
((appCode__equ__'AppCode1')||(appCode__equ__'AppCode2')) && appContext__equ__/.*dev.*/ && type__nequ__'3'
用户可以通过UI 表单去动态生成不同的filter。
我的代码实现如下
1、实体
假设是ApplicationDashboard 表,字段如下ApplicationDashboard
的属性字段。
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApplicationDashboard {
private String appCode;
private String appName;
private String appContext;
private String appId;
private String type;
private Date updateDate;
}
最终的测试代码
import top.yumbo.entity.ApplicationDashboard;
import top.yumbo.util.CustomFilter;
import java.util.Arrays;
import java.util.List;
public class CustomFilterDemo {
public static void main(String[] args) throws Exception {
/**
* 翻译:
* AppCode为 AppCode1 或 AppCode2
* context包含 dev
* type 不为 3
*/
List<CustomFilter> filters = Arrays.asList(
CustomFilter.builder().columns(ApplicationDashboard::getAppCode).value("AppCode1,AppCode2").operation(OperationEnum.EQUAL_TO).asList(true).build(),
CustomFilter.builder().columns(ApplicationDashboard::getAppContext).value("dev").build(),
CustomFilter.builder().columns(ApplicationDashboard::getType).value("3").operation(OperationEnum.EQUAL_TO).invert(true).build()
);
// ((appCode__equ__'AppCode1')||(appCode__equ__'AppCode2'))&&appContext__equ__/.*dev.*/&&type__nequ__'3'
System.out.println(CustomFilter.getFilter(filters));
}
}
CustomFilter
的实现
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.springframework.util.StringUtils;
import top.yumbo.entity.ApplicationDashboard;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CustomFilter {
private SFunction<ApplicationDashboard, String> columns;
private String value;
@Builder.Default
private boolean ignore = true;
private boolean asList;
private boolean invert;// 反
@Builder.Default
private OperationEnum operation = OperationEnum.CONTAINS;
private String getOperation() {
if (invert) {
return "__nequ__";
} else {
return "__equ__";
}
}
private String getVal(String value){
return operation.getOperationValue(value);
}
public String getFilter() {
String result = "";
if (StringUtils.hasText(value)) {
LambdaMeta lambdaMeta = LambdaUtils.extract(columns);
String filedName = PropertyNamer.methodToProperty(lambdaMeta.getImplMethodName());
if (asList) {
List<String> valList = Arrays.asList(value.split(","));
List<String> list = valList.stream().map(String::trim)
.map(val -> "(" + filedName + getOperation() + getVal(val) + ")")
.collect(Collectors.toList());
return "(" + mergeList(list, "||") + ")";
} else {
return filedName + getOperation() + getVal(value);
}
}
return result;
}
public static String getFilter(List<CustomFilter> filters) {
return getFilter(filters, "&&");
}
public static String getFilter(List<CustomFilter> filters, String concat) {
List<String> filterList = filters.stream().map(CustomFilter::getFilter).collect(Collectors.toList());
return mergeList(filterList, concat);
}
public static String mergeList(List<String> list, String concat) {
if (list != null && list.size() > 0) {
return list.size() == 1 ? list.get(0) : list.stream().reduce((str1, str2) -> str1 + concat + str2).get();
}
return "";
}
}
OperationEnum
import java.util.function.Function;
public enum OperationEnum {
EQUAL_TO((value) -> "'" + value + "'"),
CONTAINS((value) -> "/.*" + value + ".*/"),
START_WITH((value) -> "/" + value + ".*/"),
END_WITH((value) -> "/.*" + value + "/");
private final Function<String, String> function;
OperationEnum(Function<String, String> function) {
this.function = function;
}
public String getOperationValue(String value) {
return function.apply(value);
}
}