代码&优雅着&生活

导航

使用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 2021-01-13 19:23  幸运的凌人  阅读(1404)  评论(3编辑  收藏  举报