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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM