使用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语句了。
业务驱动技术,技术是手段,业务是目的。