背景:
由於數據中台中涉及到根據條件生成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語句了。