Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接

这篇具有很好参考价值的文章主要介绍了Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

ps:最近在参与3100保卫战,战况很激烈,刚刚打完仗,来更新一下之前写了一半的博客。

该篇针对日常写查询的时候,那些动态条件sql 做个简单的封装,自动生成(抛砖引玉,搞个小玩具,不喜勿喷)。

正文

来看看我们平时写那些查询,基本上都要写的一些动态sql:
 

Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接,跟我一起玩转 SpringBoot,Mybatis,spring boot,mybatis,java,自定义拦截器,动态sql

一个字段写一个if ,有没有人觉得烦的。

每张表的查询,很多都有这种需求,根据什么查询,根据什么查询,不为空就触发条件。

天天写天天写,copy 改,copy改, 有没有人觉得烦的。


Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接,跟我一起玩转 SpringBoot,Mybatis,spring boot,mybatis,java,自定义拦截器,动态sql

可能有看官看到这就会说, 用插件自动生成就好了。
也有看官会说,用mybatis-plus就好了。

确实有道理,但是我就是想整个小玩具。你管我。

开整

本篇实现的封装小玩具思路:

①制定的规则(比如标记自定义注解 @JcSqlQuery 或是 函数命名带上JcDynamics)。

② 触发的查询符合规则的, 都自动去根据传参对象,不为空就自动组装 sql查询条件。

③ 利用mybatis @Select 注解,把默认表查询sql写好,顺便进到自定义的mybatis拦截器里面。

④组装完sql,就执行,完事。

先写mapper函数 :
 

/**
 * @Author JCccc
 * @Description
 * @Date 2023/12/14 16:56
 */
@Mapper
public interface DistrictMapper {

    @Select("select code,name,parent_code,full_name  FROM s_district_info")
    List<District> queryListJcDynamics(District district);

    @Select("select code,name,parent_code,full_name  FROM s_district_info")
    District queryOneJcDynamics(District district);

}

Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接,跟我一起玩转 SpringBoot,Mybatis,spring boot,mybatis,java,自定义拦截器,动态sql

然后是ParamClassInfo.java 这个用于收集需要参与动态sql组装的类:

 

import lombok.Data;

/**
 * @Author JCccc
 * @Description
 * @Date 2021/12/14 16:56
 */
@Data
public class ParamClassInfo {

    private  String classType;
    private  Object keyValue;
    private  String  keyName;

}

然后是一个自定义的mybatis拦截器(这里面写了一些小函数实现自主组装,下面有图解) :


MybatisInterceptor.java

import com.example.dotest.entity.ParamClassInfo;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.*;


/**
 * @Author JCccc
 * @Description
 * @Date 2021/12/14 16:56
 */
@Component
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MybatisInterceptor implements Interceptor {


    private final static String JC_DYNAMICS = "JcDynamics";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取执行参数
        Object[] objects = invocation.getArgs();
        MappedStatement ms = (MappedStatement) objects[0];
        Object objectParam = objects[1];
        List<ParamClassInfo> paramClassInfos = convertParamList(objectParam);
        String queryConditionSqlScene = getQueryConditionSqlScene(paramClassInfos);
        //解析执行sql的map方法,开始自定义规则匹配逻辑
        String mapperMethodAllName = ms.getId();
        int lastIndex = mapperMethodAllName.lastIndexOf(".");
        String mapperClassStr = mapperMethodAllName.substring(0, lastIndex);
        String mapperClassMethodStr = mapperMethodAllName.substring((lastIndex + 1));
        Class<?> mapperClass = Class.forName(mapperClassStr);
        Method[] methods = mapperClass.getMethods();
        for (Method method : methods) {
            if (method.getName().equals(mapperClassMethodStr) && mapperClassMethodStr.contains(JC_DYNAMICS)) {
                BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);
                String originalSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ");
                //进行自动的 条件拼接
                String newSql = originalSql + queryConditionSqlScene;
                BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,
                        boundSql.getParameterMappings(), boundSql.getParameterObject());
                MappedStatement newMs = newMappedStatement(ms, new MyBoundSqlSqlSource(newBoundSql));
                for (ParameterMapping mapping : boundSql.getParameterMappings()) {
                    String prop = mapping.getProperty();
                    if (boundSql.hasAdditionalParameter(prop)) {
                        newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
                    }
                }
                Object[] queryArgs = invocation.getArgs();
                queryArgs[0] = newMs;
                System.out.println("打印新SQL语句" + newSql);
            }
        }
        //继续执行逻辑
        return invocation.proceed();
    }

    private String getQueryConditionSqlScene(List<ParamClassInfo> paramClassInfos) {
        StringBuilder conditionParamBuilder = new StringBuilder();
        if (CollectionUtils.isEmpty(paramClassInfos)) {
            return "";
        }
        conditionParamBuilder.append("  WHERE ");
        int size = paramClassInfos.size();
        for (int index = 0; index < size; index++) {
            ParamClassInfo paramClassInfo = paramClassInfos.get(index);
            String keyName = paramClassInfo.getKeyName();
            //默认驼峰拆成下划线 ,比如 userName -》 user_name ,   name -> name
            //如果是需要取别名,其实可以加上自定义注解这些,但是本篇例子是轻封装,思路给到,你们i自己玩
            String underlineKeyName = camelToUnderline(keyName);
            conditionParamBuilder.append(underlineKeyName);
            Object keyValue = paramClassInfo.getKeyValue();
            String classType = paramClassInfo.getClassType();
            //其他类型怎么处理 ,可以按照类型区分 ,比如检测到一组开始时间,Date 拼接 between and等
//            if (classType.equals("String")){
//                conditionParamBuilder .append("=").append("\'").append(keyValue).append("\'");
//            }

            conditionParamBuilder.append("=").append("\'").append(keyValue).append("\'");
            if (index != size - 1) {
                conditionParamBuilder.append(" AND ");
            }
        }
        return conditionParamBuilder.toString();
    }

    private static List<ParamClassInfo> convertParamList(Object obj) {
        List<ParamClassInfo> paramClassList = new ArrayList<>();
        for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(obj.getClass())) {
            if (!"class".equals(pd.getName())) {
                if (ReflectionUtils.invokeMethod(pd.getReadMethod(), obj) != null) {
                    ParamClassInfo paramClassInfo = new ParamClassInfo();
                    paramClassInfo.setKeyName(pd.getName());
                    paramClassInfo.setKeyValue(ReflectionUtils.invokeMethod(pd.getReadMethod(), obj));
                    paramClassInfo.setClassType(pd.getPropertyType().getSimpleName());
                    paramClassList.add(paramClassInfo);
                }
            }
        }
        return paramClassList;
    }


    public static String camelToUnderline(String line){
        if(line==null||"".equals(line)){
            return "";
        }
        line=String.valueOf(line.charAt(0)).toUpperCase().concat(line.substring(1));
        StringBuffer sb=new StringBuffer();
        Pattern pattern= compile("[A-Z]([a-z\\d]+)?");
        Matcher matcher=pattern.matcher(line);
        while(matcher.find()){
            String word=matcher.group();
            sb.append(word.toUpperCase());
            sb.append(matcher.end()==line.length()?"":"_");
        }
        return sb.toString();
    }


    @Override
    public Object plugin(Object o) {
        //获取代理权
        if (o instanceof Executor) {
            //如果是Executor(执行增删改查操作),则拦截下来
            return Plugin.wrap(o, this);
        } else {
            return o;
        }
    }

    /**
     * 定义一个内部辅助类,作用是包装 SQL
     */
    class MyBoundSqlSqlSource implements SqlSource {
        private BoundSql boundSql;

        public MyBoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }

    }

    private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new
                MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {
            builder.keyProperty(ms.getKeyProperties()[0]);
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }


    @Override
    public void setProperties(Properties properties) {
        //读取mybatis配置文件中属性
    }


代码简析:

驼峰转换下划线,用于转出数据库表的字段 :

Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接,跟我一起玩转 SpringBoot,Mybatis,spring boot,mybatis,java,自定义拦截器,动态sql

通过反射把 sql入参的对象 不为空的属性名和对应的值,拿出来:

Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接,跟我一起玩转 SpringBoot,Mybatis,spring boot,mybatis,java,自定义拦截器,动态sql

组件动态查询的sql 语句 :

Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接,跟我一起玩转 SpringBoot,Mybatis,spring boot,mybatis,java,自定义拦截器,动态sql

写个简单测试用例:

    @Autowired
    DistrictMapper districtMapper;

    @Test
    public void test() {
        District query = new District();
        query.setCode("110000");
        query.setName("北京市");
        District district = districtMapper.queryOneJcDynamics(query);
        System.out.println(district.toString());

        District listQuery = new District();
        listQuery.setParentCode("110100");
        List<District> districts = districtMapper.queryListJcDynamics(listQuery);
        System.out.println(districts.toString());
    }

 看下效果,可以看到都自动识别把不为空的字段属性和值拼接成查询条件了:

Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接,跟我一起玩转 SpringBoot,Mybatis,spring boot,mybatis,java,自定义拦截器,动态sql

 Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接,跟我一起玩转 SpringBoot,Mybatis,spring boot,mybatis,java,自定义拦截器,动态sql

好了,该篇就到这。 抛砖引玉,领悟分步封装思路最重要,都去搞些小玩具娱乐娱乐吧。文章来源地址https://www.toymoban.com/news/detail-655806.html

到了这里,关于Springboot 封装整活 Mybatis 动态查询条件SQL自动组装拼接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java springboot整合MyBatis实现分页查询以及带条件的分页查询

    之前的文章 java springboot整合MyBatis做数据库查询操作操作了springboot整合MyBatis,然后简单做了个按id查询的操作 那么 我们按上文搭建起的环境继续 我们直接在staffDao接口中声明一个分页函数 这里 我们直接在 sql语句中写limit 分页逻辑 参数是方法接收的 这个函数接收两个参数

    2024年02月10日
    浏览(45)
  • MyBatis多表查询+动态sql

    在全局配置文件中中设置MyBatis执行日志 假设有一个用户表和文章表,实体类中一个关联关系。 用户实体类 文章实体类 如果想查询的结果包含UserInfo的信息就需要使用,⼀对⼀映射要使⽤ association 标签,因为一篇文章只能对应一个作者。 Controller控制器代码 Service服务层代码

    2023年04月19日
    浏览(33)
  • mybatis xml多表查询,子查询,连接查询,动态sql

    student_type 表 student 表 Student 类 一个学生只有一个年级 Type 类 一个年级有多个学生,所以用 list 下列代码中: 1 resultMap 里面property对应实体类属性,column对应数据库字段名 2 主键用 id 标签 其他用result 3 关联查询(子查询和连接查询) 连接查询查一次 4 一个年级多个学生,所以

    2024年01月21日
    浏览(57)
  • MyBatis(多表查询,动态SQL的使用)

    目录 多表查询  查询文章详情 查询一个用户底下的所有文章 动态SQL的使用 if 标签 trim 标签  where 标签 set 标签 foreach 标签 现在有俩张表,一张是 文章表 ,一张是 用户表 .如下: 我们现在想查询得到一张表,表里面的内容和文章表大多一致,只是要 在文章表的基础上添加用户表中

    2024年02月10日
    浏览(45)
  • MyBatis动态SQL、模糊查询与结果映射

    目录 前言 一、MyBatis动态SQL 1.动态SQL是什么 2.动态SQL的作用 3.常用动态SQL元素 1. where + if 元素 2. set + if 元素 3. choose + when + otherwise 元素 4. 自定义 trim 元素  1. 自定义 trim 元素改写上面的 where + if 语句 2. 自定义 trim 元素改写上面的 set + if 语句 5. foreach 元素 6.SQL片段重用 二、

    2024年02月11日
    浏览(37)
  • 【MyBatis 学习三】子段不一致问题 && 多表查询 && 动态SQL

    目录 一、解决Java实体类属性与数据库表字段不一致问题 🌷现象1:显示字段不对应:使用ResultType查询结果为null; 🌷解决办法:字段不对应:使用ResultMap解决。 二、数据库的多表查询 🌷方式1:使用对象user  🌷方式2:直接写具体的属性 三、动态SQL的使用 🌷1、if标签:单

    2024年02月15日
    浏览(38)
  • Mybatis-Plus详解(新建maven项目、查询所有信息、打印SQL日志、实现CRUD(增删改查)、分页、条件查询且分页,前后端分离式开发)

    MyBatis-Plus(opens new window) (简称MP) 是一个MyBatis(opens new window)的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。想查看官网相关内容的化我这里提供了官网地址:https://baomidou.com/ 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般

    2024年02月04日
    浏览(68)
  • MyBatis进阶:掌握MyBatis动态SQL与模糊查询、结果映射,让你在面试中脱颖而出!!

    目录 一、引言 二、MyBatis动态SQL 2.1.if元素使用 2.2.foreach元素使用 三、MyBatis模糊查询 ①使用#{字段名} ②使用${字段名} ③使用concat{\\\'%\\\',#{字段名},\\\'%\\\'} 总结 四、MyBatis结果映射 4.1.案例演示 4.1.1.resultType进行结果映射 4.1.2.resultMap进行结果映射 在当今的软件开发环境中,数据库的使

    2024年02月11日
    浏览(45)
  • mybatis-plus技巧--动态表名-多语句-拼接sql--关于mybatis的mysql分页查询总数的优化思考

    传入tableName参数就可以了,不过只能用$不能用# 因为#会发生预编译,然后会在表名上加引号’\\\'。 新建一个表名拦截类实现TableNameHandler mybatisPlus添加插件 实例: 每天按统计 如果表名为count则加上今天的时间 每次设置 直接设置名字,然后就会改变的。 需要在配置文件中的

    2024年01月16日
    浏览(46)
  • Mybatis-puls——条件查询的三种格式+条件查询null判定+查询投影

    在mybatis_plus的封装中的WrapperT接口参数就是用于封装查询条件   在测试类中启动如上一个简单的查询,然后控制台运行会输出一大堆无关日志,这里先把这些日志关闭 先新建一个XML配置文件   然后变成如下,这里configuration标签里面什么都没有配置就是取消所有日志文件了

    2024年01月18日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包