Commit c1bb377f authored by shangtx's avatar shangtx

feat: 根据参数注解自动生成sql where条件方法

parent 06a10c1d
package com.onsiteservice.admin.controller.base.dto;
import com.onsiteservice.common.annotation.sql.ConditionEnum;
import com.onsiteservice.common.annotation.sql.FieldCondition;
import com.onsiteservice.dao.common.page.PageParams;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
......@@ -13,19 +15,29 @@ import java.time.LocalDateTime;
@Getter
@Setter
@ToString
@FieldCondition(alias = "t1")
public class BaseErrorDTO extends PageParams {
@ApiModelProperty("开始结束时间")
private LocalDateTime[] createTime;
@FieldCondition(column = "create_time", rel = ConditionEnum.LTEQ)
private LocalDateTime createTimeBegin;
@FieldCondition(column = "create_time", rel = ConditionEnum.GTEQ)
private LocalDateTime createTimeEnd;
@FieldCondition
@ApiModelProperty("项目名称")
private String projectName;
@FieldCondition
@ApiModelProperty("运行环境")
private String environment;
@FieldCondition
@ApiModelProperty("错误码")
private String errorCode;
@ApiModelProperty("运行平台")
@FieldCondition
private String platform;
}
package com.onsiteservice.admin.mapper.base;
import com.onsiteservice.common.annotation.sql.AutoCondition;
import com.onsiteservice.dao.common.page.PageParams;
import org.apache.ibatis.annotations.Param;
......@@ -16,5 +17,6 @@ public interface BaseErrorLogBizMapper {
/**
* 分页查询列表
*/
@AutoCondition
List<Map> getPage(@Param("param") PageParams param);
}
package com.onsiteservice.admin.mybatis;
public class AutoConditionInterceptor {
}
......@@ -7,26 +7,9 @@
select t1.*
from base_error_log t1
<where>
<if test="param.projectName != null and param.projectName != '' ">
and t1.project_name like "%"#{param.projectName}"%"
</if>
<if test="param.environment != null and param.environment != '' ">
and t1.environment like "%"#{param.environment}"%"
</if>
<if test="param.errorCode != null and param.errorCode != '' ">
and t1.error_code like "%"#{param.errorCode}"%"
</if>
<if test="param.platform != null and param.platform != '' ">
and t1.platform like "%"#{param.platform}"%"
</if>
<if test="param.createTime != null and param.createTime.length == 2 ">
and t1.create_time <![CDATA[>]]> #{param.createTime[0]}
and t1.create_time <![CDATA[<=]]> #{param.createTime[1]}
</if>
<include refid="com.onsiteservice.dao.mapper.base.CommonSnippet.condition">
<property name="param" value="param"/>
</include>
</where>
order by
<if test="param.sort != null and param.sort != ''">
......@@ -34,4 +17,5 @@
</if>
t1.id desc, t1.create_time desc
</select>
</mapper>
package com.onsiteservice.common.annotation.sql;
import java.lang.annotation.*;
/**
* 用于Mybatis Mapper, 根据参数自动生成查询条件
* 仅检查第一个参数
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Inherited
@Documented
public @interface AutoCondition {
}
package com.onsiteservice.common.annotation.sql;
import com.google.common.base.CaseFormat;
import com.onsiteservice.util.ReflectUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
@Slf4j
@Aspect
@Component
public class AutoConditionAspect {
private static final String CONDITIONS = "conditions";
@Pointcut("@annotation(com.onsiteservice.common.annotation.sql.AutoCondition)")
public void autoCondition() {
}
@Around("autoCondition()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
var args = joinPoint.getArgs();
appendConditions(args);
Object result = joinPoint.proceed(args);
return result;
}
/**
* 添加条件
*/
private void appendConditions(Object[] args) {
if (args == null || args.length == 0) {
return;
}
var arg = args[0];
// 先获取字段中的注解值
var conditions = extractConditionInfo(arg);
// 将条件集合放回对象
if (arg instanceof AutoSqlCondition) {
// 如果实现了AutoSqlCondition接口则直接强转赋值
((AutoSqlCondition) arg).setConditions(conditions);
} else {
// 如果没有实现AutoSqlCondition接口,则使用cglib添加一个名为conditions的属性
var newArg = addConditionsToObj(arg);
BeanUtils.copyProperties(arg, newArg);
ReflectUtils.invokeSetter(newArg, CONDITIONS, conditions);
// 替换原参数
args[0] = newArg;
}
}
private Object addConditionsToObj(Object obj) {
BeanGenerator beanGenerator = new BeanGenerator();
beanGenerator.setSuperclass(obj.getClass());
beanGenerator.setUseCache(true);
beanGenerator.addProperty(CONDITIONS, List.class);
return beanGenerator.create();
}
/**
* 从对象中获取条件值
*/
private List<ConditionInfo> extractConditionInfo(Object arg) {
var filedList = arg.getClass().getDeclaredFields();
var conditions = new LinkedList<ConditionInfo>();
// 获取类型注解
var typeAnno = arg.getClass().getDeclaredAnnotation(FieldCondition.class);
String defaultAlias = null;
if(typeAnno != null) {
defaultAlias = typeAnno.alias();
}
for (Field field : filedList) {
var anno = field.getDeclaredAnnotation(FieldCondition.class);
Object value = ReflectUtils.invokeGetter(arg, field.getName());
if (value != null && anno != null) {
// 列名
String prefix;
if(StringUtils.isEmpty(anno.alias())) {
if(StringUtils.isEmpty(defaultAlias)) {
prefix = "";
} else {
prefix = defaultAlias + ".";
}
} else {
prefix = anno.alias() + ".";
}
var conditionInfo = new ConditionInfo();
conditionInfo.setValue(value);
if (StringUtils.isEmpty(anno.column())) {
// 没有指定列名,使用字段名称转下划线
conditionInfo.setColumn(prefix + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()));
} else {
conditionInfo.setColumn(prefix + anno.column());
}
// 比较方式
if (anno.rel().equals(ConditionEnum.NONE)) {
// 没有指定比较方式,根据类型自动判断
if (value instanceof String) {
conditionInfo.setRel(ConditionEnum.LIKE.getOperator());
} else {
conditionInfo.setRel(ConditionEnum.EQ.getOperator());
}
} else {
conditionInfo.setRel(anno.rel().getOperator());
}
conditions.add(conditionInfo);
}
}
return conditions;
}
}
package com.onsiteservice.common.annotation.sql;
import com.onsiteservice.common.annotation.sql.ConditionInfo;
import java.util.ArrayList;
import java.util.List;
public interface AutoSqlCondition {
List<ConditionInfo> conditions = new ArrayList<>();
default List<ConditionInfo> getConditions() {
return conditions;
}
default void setConditions(List<ConditionInfo> list) {
conditions.clear();
conditions.addAll(list);
}
}
package com.onsiteservice.common.annotation.sql;
import lombok.Getter;
public enum ConditionEnum {
NONE(""),
/**
* 等于
*/
EQ("="),
/**
* 大于
*/
GT(">"),
/**
* 小于
*/
LT("<"),
/**
* 大于等于
*/
GTEQ(">="),
/**
* 小于等于
*/
LTEQ("<="),
/**
* 左右模糊
*/
LIKE("like");
@Getter
private final String operator;
ConditionEnum(String operator) {
this.operator = operator;
}
}
package com.onsiteservice.common.annotation.sql;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@NoArgsConstructor
@Setter
@Getter
public class ConditionInfo {
// 值
private Object value;
// 比较类型
private String rel;
// 字段名
private String column;
}
package com.onsiteservice.common.annotation.sql;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
@Inherited
@Documented
public @interface FieldCondition {
String value() default "";
/**
* 比较方法
*/
ConditionEnum rel() default ConditionEnum.NONE;
/**
* 字段名称
*/
String column() default "";
/**
* 查询别名
*/
String alias() default "";
}
package com.onsiteservice.util;
import com.onsiteservice.common.annotation.sql.AutoSqlCondition;
import com.onsiteservice.util.ReflectUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanGenerator;
@Slf4j
public class ProxyUtil {
/**
* 为对象新增一个属性
* @param object 原对象
* @param propName 新增属性名
* @param clazz 新增属性类型
* @param value 新增属性值
* @return 修改后的对象
*/
public static Object addProperty(Object object, String propName, Class clazz, Object value) {
BeanGenerator beanGenerator = new BeanGenerator();
beanGenerator.setSuperclass(object.getClass());
beanGenerator.setUseCache(true);
beanGenerator.addProperty(propName, clazz);
var newObj = beanGenerator.create();
// 复制属性
BeanUtils.copyProperties(object, newObj);
// 新增属性值
ReflectUtils.invokeSetter(newObj, propName, value);
return newObj;
}
public static void main(String[] args) {
/* var pojo = new Pojo(1, "Tom");
var pojoPlus = addProperty(pojo, "age", Integer.class, 21);
System.out.println(JSONObject.toJSONString(pojo));
System.out.println(JSONObject.toJSONString(pojoPlus));*/
/* var foo = new Foo(23, "Clan");
foo.setConditions(List.of("a", "b", "c"));
System.out.println(JSONObject.toJSONString(foo)); */
/* var fields = BaseErrorDTO.class.getDeclaredFields();
System.out.println(fields[0].getDeclaredAnnotation(FieldCondition.class));
var col = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "pageSize");
System.out.println(col);*/
}
public static class Pojo {
private int id;
private String name;
public Pojo() {
}
public Pojo(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class Foo implements AutoSqlCondition {
private int id;
private String name;
public Foo() {
}
public Foo(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
package com.onsiteservice.dao.mapper.base;
public interface CommonSnippet {
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.onsiteservice.dao.mapper.base.CommonSnippet">
<!-- 自动生成sql条件 -->
<sql id="condition">
<foreach collection="param.conditions" item="condition">
and ${condition.column} ${condition.rel}
<choose>
<when test="condition.rel == 'like'">
"%"#{condition.value, jdbcType=VARCHAR}"%"
</when>
<otherwise>#{condition.value}</otherwise>
</choose>
</foreach>
</sql>
</mapper>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment