百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 博客教程 > 正文

利用反射跟自定义注解拼接实体对象的查询SQL

connygpt 2024-10-25 11:35 6 浏览

  前言

  项目中虽然有ORM映射框架来帮我们拼写SQL,简化开发过程,降低开发难度。但难免会出现需要自己拼写SQL的情况,这里分享一个利用反射跟自定义注解拼接实体对象的查询SQL的方法。


  代码

  自定义注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Like {

}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Between {

    /**
     * 最小值的实体属性名
     */
    String min();

    /**
     * 最大值的实体属性名
     */
    String max();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface In {

    /**
     * in的具体集合的属性名
     */
    String values();
}


  实体对象:

@Data
@Entity
@Table(name = "RES_LOG")
public class ResLog {
    @Id
    private String logId;
    private String resourceType;
    private String resourceId;
    @Like //开启模糊查询
    private String resourceName;
    private String resourceCode;
    @In(values = "operationTypeList")//in查询
    private String operationType;
    @Between(min = "operationTimeStart", max = "operationTimeEnd")//开启区间查询
    private Date operationTime;
    private String operatorId;
    private String operator;

    @Transient
    private Date operationTimeStart;
    @Transient
    private Date operationTimeEnd;
    @Transient
    private List<String> operationTypeList;

}


  拼接SQL方法:

/**
 * 自动拼接原生SQL的“and”查询条件,支持自定义注解:@Like @Between @In
 *
 * @param entity           实体对象
 * @param sql              待拼接SQL
 * @param ignoreProperties 忽略属性
 */
public static void appendQueryColumns(Object entity, StringBuilder sql, String... ignoreProperties) {

    try {
        //忽略属性
        List<String> ignoreList1 = Arrays.asList(ignoreProperties);
        //默认忽略分页参数
        List<String> ignoreList2 = Arrays.asList("class", "pageable", "page", "rows", "sidx", "sord");
   
        //反射获取Class的属性(Field表示类中的成员变量)
        for (Field field : entity.getClass().getDeclaredFields()) {
            //获取授权
            field.setAccessible(true);
            //属性名称
            String fieldName = field.getName();
            //属性的值
            Object fieldValue = field.get(entity);
            //检查Transient注解,是否忽略拼接
            if (!field.isAnnotationPresent(Transient.class)) {
                String column = new PropertyNamingStrategy.SnakeCaseStrategy().translate(fieldName).toLowerCase();
                //值是否为空
                if (!StringUtils.isEmpty(fieldValue)) {
                    //映射关系:对象属性(驼峰)->数据库字段(下划线)
                    if (!ignoreList1.contains(fieldName) && !ignoreList2.contains(fieldName)) {
                        //开启模糊查询
                        if (field.isAnnotationPresent(Like.class)) {
                            sql.append(" and " + column + " like '%" + escapeSql(fieldValue) + "%'");
                        }
                        //开启等值查询
                        else {
                            sql.append(" and " + column + " = '" + escapeSql(fieldValue) + "'");
                        }
                    }
                } else {
                    //开启区间查询
                    if (field.isAnnotationPresent(Between.class)) {
                        //获取最小值
                        Field minField = entity.getClass().getDeclaredField(field.getAnnotation(Between.class).min());
                        minField.setAccessible(true);
                        Object minVal = minField.get(entity);
                        //获取最大值
                        Field maxField = entity.getClass().getDeclaredField(field.getAnnotation(Between.class).max());
                        maxField.setAccessible(true);
                        Object maxVal = maxField.get(entity);
                        //开启区间查询
                        if (field.getType().getName().equals("java.util.Date")) {
                            if (!StringUtils.isEmpty(minVal)) {
                                sql.append(" and " + column + " > to_date( '" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) minVal) + "','yyyy-mm-dd hh24:mi:ss')");
                            }
                            if (!StringUtils.isEmpty(maxVal)) {
                                sql.append(" and " + column + " < to_date( '" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) maxVal) + "','yyyy-mm-dd hh24:mi:ss')");
                            }
                        }
                    }
                    
                    //开启in查询
                    if (field.isAnnotationPresent(In.class)) {
                        //获取要in的值
                        Field values = entity.getClass().getDeclaredField(field.getAnnotation(In.class).values());
                        values.setAccessible(true);
                        List<String> valuesList = (List<String>) values.get(entity);
                        if (valuesList != null && valuesList.size() > 0) {
                            String inValues = "";
                            for (String value : valuesList) {
                                inValues = inValues + "'" + value + "'";
                            }
                            sql.append(" and " + column + " in (" + escapeSql(inValues) + ")");
                        }
                    }
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}


  2019-10-24补充:注意!我们这属于动态拼写SQL,需要进行转义防范SQL注入!

/**
     * sql转义
     */
    public static String escapeSql(String str) {
        if (str == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char src = str.charAt(i);
            switch (src) {
                case '\'':
                    sb.append("''");// hibernate转义多个单引号必须用两个单引号
                    break;
                case '\"':
                case '\\':
                    sb.append('\\');
                default:
                    sb.append(src);
                    break;
            }
        }
        return sb.toString();
    }


  测试与效果

public static void main(String[] args) {
    ResLog resLog = new ResLog();
    resLog.setLogId("id1");//等值查询
    resLog.setResourceName("name1");//like查询
    resLog.setOperationTimeStart(new Date());//日期区间查询
    resLog.setOperationTimeEnd(new Date());
    ArrayList<String> list = new ArrayList<>();
    list.add("type1");
    list.add("type2");
    resLog.setOperationTypeList(list);//in查询
    //在外面拼写select * from 是为了多表联查时的情况
    StringBuilder sql = new StringBuilder("select * from res_log where '1' = '1'");
    appendQueryColumns(resLog,sql);
    System.out.println(sql.toString());
}


  拼接结果:

select *
  from res_log
 where '1' = '1'
   and log_id = 'id1'
   and resource_name like '%name1%'
   and operation_type in ('type1''type2')
   and operation_time >
       to_date('2018-10-08 15:00:40', 'yyyy-mm-dd hh24:mi:ss')
   and operation_time <
       to_date('2018-10-08 15:00:40', 'yyyy-mm-dd hh24:mi:ss')


  后记

  甚至我们可以直接获取实体对象对应的表名,直接在方法里面拼出 select * from ,这样就不需要在外面拼接这一句

//获取实体对象对应的表名
String TableName = entity.getClass().getAnnotation(Table.class).name();
System.out.println(TableName);

  为了优化SQL,一般我们不建议select * from,而是需要查询那些字段就拼出那些字段,例如:select log_id from

  但是如果数据表有一百个字段呢?一个个手动拼接就太傻了,因此写了一个自动拼接字段的方法,支持配置忽略拼接的字段

/**
     *
     * @param entity 实体对象
     * @param ignoreProperties 动态参数  忽略拼接的字段
     * @return sql
     */
    public static StringBuilder appendFields(Object entity, String... ignoreProperties) {
        StringBuilder sql = new StringBuilder();
        List<String> ignoreList = Arrays.asList(ignoreProperties);
        try {
            sql.append("select ");

            for (Field field : entity.getClass().getDeclaredFields()) {
                //获取授权
                field.setAccessible(true);
                String fieldName = field.getName();//属性名称
                Object fieldValue = field.get(entity);//属性的值
                //非临时字段、非忽略字段
                if (!field.isAnnotationPresent(Transient.class) && !ignoreList.contains(fieldName)) {
                    //拼接查询字段  驼峰属性转下划线
                    sql.append(new PropertyNamingStrategy.SnakeCaseStrategy().translate(fieldName).toLowerCase()).append(" ").append(",");
                }
            }
            //处理逗号(删除最后一个字符)
            sql.deleteCharAt(sql.length() - 1);

            String tableName = entity.getClass().getAnnotation(Table.class).name();
            sql.append("from ").append(tableName).append(" where '1' = '1' ");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return sql;
    }

  接着上面的main测试

public static void main(String[] args) {
    ResLog resLog = new ResLog();
    resLog.setLogId("id1");//等值查询
    resLog.setResourceName("name1");//like查询
    resLog.setOperationTimeStart(new Date());//日期区间查询
    resLog.setOperationTimeEnd(new Date());
    ArrayList<String> list = new ArrayList<>();
    list.add("type1");
    list.add("type2");
    resLog.setOperationTypeList(list);//in查询
    //动态拼接查询字段
    StringBuilder sql = appendFields(resLog,"remark","operator");
    appendQueryColumns(resLog,sql);
    System.out.println(sql.toString());
}

  结果

select log_id,
       resource_type,
       resource_id,
       resource_name,
       resource_code,
       operation_type,
       operation_time,
       operator_id
  from RES_LOG
 where '1' = '1'
   and log_id = 'id1'
   and resource_name like '%name1%'
   and operation_type in ('type1''type2')
   and operation_time >
       to_date('2018-12-13 10:34:33', 'yyyy-MM-dd hh24:mi:ss')
   and operation_time <
       to_date('2018-12-13 10:34:33', 'yyyy-MM-dd hh24:mi:ss')



  更新

  2020-10-10更新

  在之前的操作中,我们直接在与数据表映射的entity实体类中使用自定义注解,实体类负责与数据表进行映射,具有共有属性,不应该被业务污染,而实体类对应Vo类负责接参、传参等传输数据的责任,不同的业务冲突时可以创建多个Vo类来解决,正合适我们使用自定义注解来拼接SQL,因此,改成在Vo类中使用我们的自定义注解


  首先,SqlUtil类需要进行一些调整,主要是对appendQueryColumns方法的调整,同时新增了一个拼写全部SQL的聚合方法joinSqlByEntityAndVo

package cn.huanzi.qch.baseadmin.util;

import cn.huanzi.qch.baseadmin.annotation.Between;
import cn.huanzi.qch.baseadmin.annotation.In;
import cn.huanzi.qch.baseadmin.annotation.Like;
import cn.huanzi.qch.baseadmin.common.pojo.PageCondition;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.Transient;
import org.springframework.util.StringUtils;

import javax.persistence.Table;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 拼接SQL工具类
 * 详情请阅读博客:https://www.cnblogs.com/huanzi-qch/p/9754846.html
 */
@Slf4j
public class SqlUtil {

    /**
     * 日期转换格式
     */
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 数据库驱动类,用于判断数据库类型
     * MySQL:com.mysql.cj.jdbc.driver(默认)
     * postgresql:org.postgresql.driver
     * Oracle:oracle.jdbc.oracledriver
     */
    @Value("${string.datasource.driver-class-name:com.mysql.cj.jdbc.Driver}")
    private static String sqlType;

    /**
     * 根据实体、Vo直接拼接全部SQL
     * @param entityClass 实体类
     * @param entityVo    继承了PageCondition分页条件的Vo类
     * @return sql
     */
    public static StringBuilder joinSqlByEntityAndVo(Class<?> entityClass,Object entityVo){
        //select 所有字段 from table
        StringBuilder sql = SqlUtil.appendFields(entityClass);

        //拼接查询字段
        SqlUtil.appendQueryColumns(entityClass,entityVo,sql);

        //拼接排序字段
        SqlUtil.orderByColumn((PageCondition)entityVo,sql);

        return sql;
    }

    /**
     * 自动拼接原生SQL的“and”查询条件,
     * 支持自定义注解,注解改成打在vo类中,不应该破坏公用的entity实体映射类:@Like @Between @In
     *
     * @param entityClass      实体类
     * @param entityVo         继承了PageCondition分页条件的Vo类
     * @param sql              待拼接SQL
     * @param ignoreProperties 忽略属性
     */
    public static void appendQueryColumns(Class<?> entityClass, Object entityVo, StringBuilder sql, String... ignoreProperties) {
        try {

            List<String> ignoreList1 = Arrays.asList(ignoreProperties);
            //默认忽略分页参数
            List<String> ignoreList2 = Arrays.asList("class", "pageable", "page", "rows", "sidx", "sord");

            //反射获取Class的属性(Field表示类中的成员变量)
            Class<?> entityVoClass = entityVo.getClass();

            //可以直接传进来,也可以根据entityVoClass来创建entityClass,如果选择动态拼接,对命名规则有一定要求
//            Class<?> entityClass = Class.forName(entityVoClass.getName().replaceFirst("Vo",""));

            for (Field field : entityVoClass.getDeclaredFields()) {
                //获取授权
                field.setAccessible(true);
                //属性名称
                String fieldName = field.getName();
                //属性的值
                Object fieldValue = field.get(entityVo);

                //检查entity中是否也存在该字段,如果没有,直接跳过
                try {
                    entityClass.getDeclaredField(fieldName);
                }catch (NoSuchFieldException e){
                    log.debug("entity中没有这个字段,拼接查询SQL直接跳过:" + e.getMessage());
                    continue;
                }

                String column = SqlUtil.translate(fieldName);

                //值是否为空
                if (!StringUtils.isEmpty(fieldValue)) {
                    //映射关系:对象属性(驼峰)->数据库字段(下划线)
                    if (!ignoreList1.contains(fieldName) && !ignoreList2.contains(fieldName)) {
                        //开启模糊查询
                        if (field.isAnnotationPresent(Like.class)) {
                            sql.append(" and ").append(column).append(" like '%").append(SqlUtil.escapeSql((String) fieldValue)).append("%'");
                        }
                        //开启等值查询
                        else {
                            sql.append(" and ").append(column).append(" = '").append(SqlUtil.escapeSql((String) fieldValue)).append("'");
                        }
                    }
                } else {
                    //开启区间查询
                    if (field.isAnnotationPresent(Between.class)) {
                        //获取最小值
                        Field minField = entityVoClass.getDeclaredField(field.getAnnotation(Between.class).min());
                        minField.setAccessible(true);
                        Object minVal = minField.get(entityVo);
                        //获取最大值
                        Field maxField = entityVoClass.getDeclaredField(field.getAnnotation(Between.class).max());
                        maxField.setAccessible(true);
                        Object maxVal = maxField.get(entityVo);
                        //开启区间查询,需要使用对应的函数
                        if (field.getType().getName().equals("java.util.Date")) {
                            //MySQL
                            if(sqlType.toLowerCase().contains("com.mysql.cj.jdbc.driver")){
                                if (!StringUtils.isEmpty(minVal)) {
                                    sql.append(" and ").append(column).append(" > str_to_date( '").append(simpleDateFormat.format((Date) minVal)).append("','%Y-%m-%d %H:%i:%s')");
                                }
                                if (!StringUtils.isEmpty(maxVal)) {
                                    sql.append(" and ").append(column).append(" < str_to_date( '").append(simpleDateFormat.format((Date) maxVal)).append("','%Y-%m-%d %H:%i:%s')");
                                }
                            }
                            //postgresql
                            if(sqlType.toLowerCase().contains("org.postgresql.driver")){
                                if (!StringUtils.isEmpty(minVal)) {
                                    sql.append(" and ").append(column).append(" > cast('").append(simpleDateFormat.format((Date) minVal)).append("' as timestamp)");
                                }
                                if (!StringUtils.isEmpty(maxVal)) {
                                    sql.append(" and ").append(column).append(" < cast('").append(simpleDateFormat.format((Date) maxVal)).append("' as timestamp)");
                                }
                            }
                            //Oracle
                            if(sqlType.toLowerCase().contains("oracle.jdbc.oracledriver")){
                                if (!StringUtils.isEmpty(minVal)) {
                                    sql.append(" and ").append(column).append(" > to_date( '").append(simpleDateFormat.format((Date) minVal)).append("','yyyy-mm-dd hh24:mi:ss')");
                                }
                                if (!StringUtils.isEmpty(maxVal)) {
                                    sql.append(" and ").append(column).append(" < to_date( '").append(simpleDateFormat.format((Date) maxVal)).append("','yyyy-mm-dd hh24:mi:ss')");
                                }
                            }
                        }
                    }

                    //开启in查询
                    if (field.isAnnotationPresent(In.class)) {
                        //获取要in的值
                        Field values = entityVoClass.getDeclaredField(field.getAnnotation(In.class).values());
                        values.setAccessible(true);
                        List<String> valuesList = (List<String>) values.get(entityVo);
                        if (valuesList != null && valuesList.size() > 0) {
                            StringBuilder inValues = new StringBuilder();
                            for (int i = 0; i < valuesList.size(); i++) {
                                inValues.append("'").append(SqlUtil.escapeSql(valuesList.get(i))).append("'");
                                if(i < valuesList.size()-1){
                                    inValues.append(",");
                                }
                            }

                            sql.append(" and ").append(column).append(" in (").append(inValues).append(")");
                        }
                    }
                }
            }
        } catch (Exception e) {
            //输出到日志文件中
            log.error(ErrorUtil.errorInfoToString(e));
        }
    }

    /**
     *
     * @param entityClass 自动拼接实体类
     * @param ignoreProperties 动态参数  忽略拼接的字段
     * @return sql
     */
    public static StringBuilder appendFields(Class<?> entityClass, String... ignoreProperties) {
        StringBuilder sql = new StringBuilder();
        List<String> ignoreList = Arrays.asList(ignoreProperties);
        sql.append("select ");

        for (Field field : entityClass.getDeclaredFields()) {
            //获取授权
            field.setAccessible(true);
            String fieldName = field.getName();//属性名称

            //非临时字段、非忽略字段
            if (!field.isAnnotationPresent(Transient.class) && !ignoreList.contains(fieldName)) {
                //拼接查询字段  驼峰属性转下划线
                sql.append(SqlUtil.translate(fieldName)).append(" ").append(",");
            }
        }
        //处理逗号(删除最后一个字符)
        sql.deleteCharAt(sql.length() - 1);

        String tableName = entityClass.getAnnotation(Table.class).name();
        sql.append("from ").append(tableName).append(" where '1' = '1' ");
        return sql;
    }

    /**
     * 拼接排序SQL
     *
     * @param pageCondition 继承了PageCondition分页条件的Vo类
     * @param sql    待拼接的SQL
     */
    public static void orderByColumn(PageCondition pageCondition, StringBuilder sql) {
        String sidx = pageCondition.getSidx();
        String sord = pageCondition.getSord();

        if (!StringUtils.isEmpty(sidx)) {
            //1.获取Bean
            BeanWrapper srcBean = new BeanWrapperImpl(pageCondition);
            //2.获取Bean的属性描述
            PropertyDescriptor[] pds = srcBean.getPropertyDescriptors();
            //3.获取符合的排序字段名
            for (PropertyDescriptor p : pds) {
                String propertyName = p.getName();
                if (sidx.equals(propertyName)) {
                    sql.append(" order by ").append(translate(sidx)).append("desc".equalsIgnoreCase(sord) ? " desc" : " asc");
                }
            }
        }
    }

    /**
     * 实体属性转表字段,驼峰属性转下划线,并全部转小写
     */
    private static String translate(String fieldName){
        return new PropertyNamingStrategy.SnakeCaseStrategy().translate(fieldName).toLowerCase();
    }

    /**
     * sql转义
     * 动态拼写SQL,需要进行转义防范SQL注入!
     */
    private static String escapeSql(String str) {
        if (str == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char src = str.charAt(i);
            switch (src) {
                case '\'':
                    sb.append("''");// hibernate转义多个单引号必须用两个单引号
                    break;
                case '\"':
                case '\\':
                    sb.append('\\');
                default:
                    sb.append(src);
                    break;
            }
        }
        return sb.toString();
    }
}


  如何使用?比如我现在有entity映射类SysUser.java,以及对应的Vo类SysUserVo.java

  entity,负责与数据表进行映射,具有共有属性,不应该被业务污染

@Entity
@Table(name = "sys_user")
@Data
public class SysUser implements Serializable {
    @Id
    private String userId;//用户id

    private String loginName;//登录名

    private String userName;//用户名称

    private String password;//登录密码

    private String valid;//软删除标识,Y/N

    private String limitedIp;//限制允许登录的IP集合

    private Date expiredTime;//账号失效时间,超过时间将不能登录系统

    private Date lastChangePwdTime;//最近修改密码时间,超出时间间隔,提示用户修改密码

    private String limitMultiLogin;//是否允许账号同一个时刻多人在线,Y/N

    private Date createTime;//创建时间

    private Date updateTime;//修改时间

}


  vo,负责传输数据,如接参、传参,当一个vo不满足多个业务需求时,可以新建多个vo类

@Data
public class SysUserVo extends PageCondition implements Serializable {

    @In(values = "userIdList")//in查询
    private String userId;//用户id

    @Like
    private String loginName;//登录名

    private String userName;//用户名称

    private String password;//登录密码

    private String valid;//软删除标识,Y/N

    private String limitedIp;//限制允许登录的IP集合

    private Date expiredTime;//账号失效时间,超过时间将不能登录系统

    private Date lastChangePwdTime;//最近修改密码时间,超出时间间隔,提示用户修改密码

    private String limitMultiLogin;//是否允许账号同一个时刻多人在线,Y/N

    @Between(min = "createTimeStart", max = "createTimeEnd")//开启区间查询
    private Date createTime;//创建时间

    private Date updateTime;//修改时间

    private String oldPassword;//修改密码时输入的旧密码

    private String newPassword;//修改密码时输入的新密码

    private Date createTimeStart;
    private Date createTimeEnd;
    private List<String> userIdList;
}


  main测试

public static void main(String[] args) {
        SqlUtil.sqlType = "com.mysql.cj.jdbc.Driver";
        SysUserVo sysUserVo = new SysUserVo();
        //like查询
        sysUserVo.setLoginName("张三");

        //日期区域查询
        sysUserVo.setCreateTimeStart(new Date());
        sysUserVo.setCreateTimeEnd(new Date());

        //in查询
        ArrayList<String> userIds = new ArrayList<>();
        userIds.add("1");
        userIds.add("2");
        sysUserVo.setUserIdList(userIds);

        //排序  asc desc
        sysUserVo.setSidx("createTime");
        sysUserVo.setSord("desc");

        //根据实体、Vo直接拼接全部SQL
        StringBuilder sql = SqlUtil.joinSqlByEntityAndVo(SysUser.class,sysUserVo);
        System.out.println(sql.toString());
    }


  结果

15:10:21.457 [main] DEBUG cn.huanzi.qch.baseadmin.util.SqlUtil - entity中没有这个字段,拼接查询SQL直接跳过:oldPassword
15:10:21.467 [main] DEBUG cn.huanzi.qch.baseadmin.util.SqlUtil - entity中没有这个字段,拼接查询SQL直接跳过:newPassword
15:10:21.467 [main] DEBUG cn.huanzi.qch.baseadmin.util.SqlUtil - entity中没有这个字段,拼接查询SQL直接跳过:createTimeStart
15:10:21.467 [main] DEBUG cn.huanzi.qch.baseadmin.util.SqlUtil - entity中没有这个字段,拼接查询SQL直接跳过:createTimeEnd
15:10:21.467 [main] DEBUG cn.huanzi.qch.baseadmin.util.SqlUtil - entity中没有这个字段,拼接查询SQL直接跳过:userIdList
select user_id ,login_name ,user_name ,password ,valid ,limited_ip ,expired_time ,last_change_pwd_time ,limit_multi_login ,create_time ,update_time from sys_user where '1' = '1'  and user_id in ('1','2') and login_name like '%张三%' and create_time > str_to_date( '2020-10-10 15:10:21','%Y-%m-%d %H:%i:%s') and create_time < str_to_date( '2020-10-10 15:10:21','%Y-%m-%d %H:%i:%s') order by create_time desc


  美化后的SQL




  业务冲突问题,场景重现:

  A只要对loginName使用@Like查询,其他全部是等值查询,但是B想要对userName使用@Like查询,其他全部是等值查询,这时候两者的业务需要体现在同一个Vo类中就冲突了,如果我们在entity映射类中使用自定义注解,这种冲突情况就不能很好地解决,因为映射类应当有且只有一个,但现在改成在Vo类中使用自定义注解,我们可以新建多个对应Vo类来解决这种冲突问题



  代码开源

  注:本文的的代码,在base-admin项目的SqlUtil.java工具类中


  代码已经开源、托管到我的GitHub、码云:

  GitHub:https://github.com/huanzi-qch/base-admin

  码云:https://gitee.com/huanzi-qch/base-admin


版权声明

作者:huanzi-qch

出处:https://www.cnblogs.com/huanzi-qch

若标题中有“转载”字样,则本文版权归原作者所有。若无转载字样,本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.

相关推荐

3分钟让你的项目支持AI问答模块,完全开源!

hello,大家好,我是徐小夕。之前和大家分享了很多可视化,零代码和前端工程化的最佳实践,今天继续分享一下最近开源的Next-Admin的最新更新。最近对这个项目做了一些优化,并集成了大家比较关注...

干货|程序员的副业挂,12个平台分享

1、D2adminD2Admin是一个完全开源免费的企业中后台产品前端集成方案,使用最新的前端技术栈,小于60kb的本地首屏js加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助...

Github标星超200K,这10个可视化面板你知道几个

在Github上有很多开源免费的后台控制面板可以选择,但是哪些才是最好、最受欢迎的可视化控制面板呢?今天就和大家推荐Github上10个好看又流行的可视化面板:1.AdminLTEAdminLTE是...

开箱即用的炫酷中后台前端开源框架第二篇

#头条创作挑战赛#1、SoybeanAdmin(1)介绍:SoybeanAdmin是一个基于Vue3、Vite3、TypeScript、NaiveUI、Pinia和UnoCSS的清新优...

搭建React+AntDeign的开发环境和框架

搭建React+AntDeign的开发环境和框架随着前端技术的不断发展,React和AntDesign已经成为越来越多Web应用程序的首选开发框架。React是一个用于构建用户界面的JavaScrip...

基于.NET 5实现的开源通用权限管理平台

??大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!??今天小编推荐一款基于.NE...

StreamPark - 大数据流计算引擎

使用Docker完成StreamPark的部署??1.基于h2和docker-compose进行StreamPark部署wgethttps://raw.githubusercontent.com/a...

教你使用UmiJS框架开发React

1、什么是Umi.js?umi,中文可发音为乌米,是一个可插拔的企业级react应用框架。你可以将它简单地理解为一个专注性能的类next.js前端框架,并通过约定、自动生成和解析代码等方式来辅助...

简单在线流程图工具在用例设计中的运用

敏捷模式下,测试团队的用例逐渐简化以适应快速的发版节奏,大家很早就开始运用思维导图工具比如xmind来编写测试方法、测试点。如今不少已经不少利用开源的思维导图组件(如百度脑图...)来构建测试测试...

【开源分享】神奇的大数据实时平台框架,让Flink&amp;Spark开发更简单

这是一个神奇的框架,让Flink|Spark开发更简单,一站式大数据实时平台!他就是StreamX!什么是StreamX大数据技术如今发展的如火如荼,已经呈现百花齐放欣欣向荣的景象,实时处理流域...

聊聊规则引擎的调研及实现全过程

摘要本期主要以规则引擎业务实现为例,陈述在陌生业务前如何进行业务深入、调研、技术选型、设计及实现全过程分析,如果你对规则引擎不感冒、也可以从中了解一些抽象实现过程。诉求从硬件采集到的数据提供的形式多种...

【开源推荐】Diboot 2.0.5 发布,自动化开发助理

一、前言Diboot2.0.5版本已于近日发布,在此次发布中,我们新增了file-starter组件,完善了iam-starter组件,对core核心进行了相关优化,让devtools也支持对IAM...

微软推出Copilot Actions,使用人工智能自动执行重复性任务

IT之家11月19日消息,微软在今天举办的Ignite大会上宣布了一系列新功能,旨在进一步提升Microsoft365Copilot的智能化水平。其中最引人注目的是Copilot...

Electron 使用Selenium和WebDriver

本节我们来学习如何在Electron下使用Selenium和WebDriver。SeleniumSelenium是ThoughtWorks提供的一个强大的基于浏览器的开源自动化测试工具...

Quick &#39;n Easy Web Builder 11.1.0设计和构建功能齐全的网页的工具

一个实用而有效的应用程序,能够让您轻松构建、创建和设计个人的HTML网站。Quick'nEasyWebBuilder是一款全面且轻巧的软件,为用户提供了一种简单的方式来创建、编辑...