代码&优雅着&生活

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

使用mybatis动态sql功能生成sql实例

背景:

由于数据中台中涉及到根据条件生成sql的需求,导致应用程序代码中许多拼接sql的程序,读起来饶了几圈,还是晕头晕脑。于是准备

使用模板技术来实现对sql的动态拼接。

目的:

使用拼接方便,可以根据接口,通过传入参数获取可以直接执行的sql语句。

用到的技术:

1.mybatis动态sql

2.动态代理

测试类:

复制代码
    @Test
    public void testProxy() throws IOException {
        CreateTaskDTO dataCheckDTO = new CreateTaskDTO();
        dataCheckDTO.setDateType(DateType.SETTLDATE.getValue());
        dataCheckDTO.setAdmdvsList(new ArrayList() {{
            add("100000");
            add("111000");
        }});
        dataCheckDTO.setDateStart(new Date());
        dataCheckDTO.setDateEnd(new Date());
        dataCheckDTO.setTaskId("11124344455");
        dataCheckDTO.setMedInsLvList(new ArrayList() {{
            add("1");
            add("2");
        }});
        TaskMapper mapper = MapperProxyFactory.getMapper(TaskMapper.class, "ruleEngine\\prepareHandle\\guojiaju.xml");
        String sql = mapper.afterFilterSql(dataCheckDTO);
        System.out.println(sql);
    }
复制代码

动态代理处理器:

复制代码
package cn.xxx.sql;

import org.apache.commons.lang3.time.DateUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

/**
 * <P>
 * 描述:遵循mybatis语法拼接sql处理器
 * </p>
 *
 * @author lishang Created on 2020/12/26 10:32
 * @version 1.0
 */
public class SqlHandller implements InvocationHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(SqlHandller.class);

    /**
     * 配置类
     */
    private Configuration configuration;

    public SqlHandller(String resource) throws IOException {
        configuration = new Configuration();
        try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
            XMLMapperBuilder builder = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            builder.parse();
        }finally {
            LOGGER.info("配置文件解析完成");
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MappedStatement mappedStatement = configuration.getMappedStatement(method.getName());
        Object param=null;
        if (args!=null && args.length>0){
            param=args[0];
        }
        BoundSql boundSql = mappedStatement.getBoundSql(param);
        //处理动态参数#{param}
        String sql = dynamicSqlHandler(boundSql);
        return sql;
    }


    /**
     * 获取参数
     *
     * @param param Object类型参数
     * @return 转换之后的参数
     */
    private static String getParameterValue(Object param) {
        if (param == null) {
            return "null";
        }
        if (param instanceof Number) {
            return param.toString();
        }
        String value = null;
        if (param instanceof String) {
            value = "'"+param.toString()+"'";;
        } else if (param instanceof Date) {
            value = ((Date)param).getTime()+"";
        } else if (param instanceof Enum) {
            value = "'"+((Enum<?>) param).name()+"'";
        } else {
            value = param.toString();
        }
        return value;
    }

    /**
     * 处理动态sql中的占位符?
     * @param boundSql
     */
    private String dynamicSqlHandler(BoundSql boundSql){
        String sql=boundSql.getSql();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        Object parameterObject = boundSql.getParameterObject();
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        if (parameterMappings != null){
            String parameter = "null";
            String propertyName;
            MetaObject newMetaObject = configuration.newMetaObject(parameterObject);
            for (ParameterMapping parameterMapping : parameterMappings) {
                if (parameterMapping.getMode() == ParameterMode.OUT) {
                    continue;
                }
                propertyName = parameterMapping.getProperty();
                if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    parameter = getParameterValue(parameterObject);
                } else if (newMetaObject.hasGetter(propertyName)) {
                    parameter = getParameterValue(newMetaObject.getValue(propertyName));
                } else if (boundSql.hasAdditionalParameter(propertyName)) {
                    parameter = getParameterValue(boundSql.getAdditionalParameter(propertyName));
                }
                sql = sql.replaceFirst("\\?", parameter);
            }
        }
        return sql;
    }



}
复制代码

代理工厂:

复制代码
package cn.xxx.sql;

import java.io.IOException;
import java.lang.reflect.Proxy;

/**
 * <P>
 * 描述:自定义拼接sql的工厂类
 * </p>
 *
 * @author lishang Created on 2020/12/26 10:35
 * @version 1.0
 */
public class MapperProxyFactory {
    /**
     * 通过此方法获取拼接sql接口的实现类
     * 此方法中的mapper.xml遵循mybatis配置文件的语法
     * @param mapperClass 接口类
     * @param resource 配置文件地址
     * @return 接口实例
     * @throws IOException
     */
    public static    <T> T getMapper(Class<T> mapperClass, String resource ) throws IOException {
        T proxy= (T) Proxy.newProxyInstance(mapperClass.getClassLoader(),new Class[]{mapperClass}, new SqlHandller(resource));
        return proxy;
    }
}
复制代码

由此,可以传入指定配置文件的路径和接口名称,获取到接口对应的实例。

调用实例中的方法,就获取到对应的sql语句了。

 

posted on   幸运的凌人  阅读(1471)  评论(3编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
历史上的今天:
2015-01-13 JS中的计时器事件
点击右上角即可分享
微信分享提示