• 使用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语句了。

  • 相关阅读:
    易拓展、易修改的状态流程设计和实现
    消除ifelse/switch语句块来聚合模型的设计与实现
    基于Http协议订阅发布系统设计
    Window mobile 实现CLIST始终选中最新添加数据
    C++ 解压zip利器
    Windows mobile 获取当前路径
    Windows mobile 根据网络地址自动拨号连接GRPS
    Windows mobile设置MFC确认键不退出及OnExit响应退出
    c++使用(Shell32.dll)GetDetailsOf 接口获取文件属性
    C++ 注册表编程
  • 原文地址:https://www.cnblogs.com/sloveling/p/mybatis_dynamic_sql.html
Copyright © 2020-2023  润新知