存储mybatis的xml标签,动态sql 查询

前言:

通过表动态存储mybatis 的xml标签,通过动态sql 入参查询,方便更新查询逻辑,无需发版即可;(当前用的是 mybatis-plus ,db用的是oracle【这个无所谓】)

注意事项:这个只适用于简单的db查询拿数据,只要sql 能实现就行,局限性 于 简单的sql 拿db里面的数据!

倘若需要在 代码里面 复杂处理的逻辑,这个方法不适用!

步骤:

  1. 查询sql标签模板;
  2. 查询参数,执行sql 查询;
  3. 返回结果;

实现步骤:

1. 表结构设计: 

        存储mybatis的xml模板(oracle)

DROP TABLE "COM_DYNAMIC_QUERY";
CREATE TABLE "COM_DYNAMIC_QUERY" (
  "ID" VARCHAR2(64 BYTE) VISIBLE NOT NULL,
  "FUN_CODE" VARCHAR2(64 BYTE) VISIBLE,
  "DESCRIPTION" VARCHAR2(512 BYTE) VISIBLE,
  "SQL_TEMPLATE" VARCHAR2(4000 BYTE) VISIBLE,
  "EXEC_NUM" NUMBER(11,0) VISIBLE,
  "CREATE_BY" VARCHAR2(128 BYTE) VISIBLE,
  "CREATE_TIME" DATE VISIBLE DEFAULT SYSDATE,
  "UPDATE_BY" VARCHAR2(128 BYTE) VISIBLE,
  "UPDATE_TIME" DATE VISIBLE DEFAULT SYSDATE,
  "REMARK" VARCHAR2(256 BYTE) VISIBLE,
  "FUN_NAME" VARCHAR2(100 BYTE) VISIBLE
)
LOGGING
NOCOMPRESS
PCTFREE 10
INITRANS 1
STORAGE (
  INITIAL 65536 
  NEXT 1048576 
  MINEXTENTS 1
  MAXEXTENTS 2147483645
  BUFFER_POOL DEFAULT
)
PARALLEL 1
NOCACHE
DISABLE ROW MOVEMENT
;
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."ID" IS '主键ID';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."FUN_CODE" IS '功能编码';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."DESCRIPTION" IS '功能描述';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."SQL_TEMPLATE" IS '模板';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."EXEC_NUM" IS '执行次数';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."CREATE_BY" IS '创建人';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."UPDATE_BY" IS '更新人';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."UPDATE_TIME" IS '更新时间';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."REMARK" IS '备注';
COMMENT ON COLUMN "COM_DYNAMIC_QUERY"."FUN_NAME" IS '功能模块';
COMMENT ON TABLE "COM_DYNAMIC_QUERY" IS '可视化动态sql 查询';

 存储mybatis的xml标签,动态sql 查询_第1张图片

 某条标签模板的示例:(注意事项,查询的sql 用query 前缀开来)

SELECT
		   p.machineName,
		   p.portName,
		   p.areaName,
		   ms.MACHINEPROCESSTYPE,
		   p.TRANSFERSTATE PORTSTATENAME,
		   enum.DESCRIPTION AS machineProcessTypecn,
		   subStr( p.LASTEVENTUSER  , 0, instr( p.LASTEVENTUSER  , ':' ) - 1 ) LASTEVENTUSER
		FROM
			port p
		INNER JOIN machinespec ms ON p.machinename = ms.machineName
		LEFT JOIN ENUMDEFVALUE enum ON ms.MACHINEPROCESSTYPE = enum.ENUMVALUE AND enum.ENUMNAME = 'Machine_type' 
		where 1=1
		
			and ms.detailMachineType = #{query.detailMachineType}
		
		order by p.machineName

2. 代码实现

  1. controller 代码:
    1.     @ApiOperation(value = "动态查询执行器")
          @PostMapping(value = "/executeDynamicQuery")
          public RestResponse executeDynamicQuery(@RequestBody HashMap dto){
              return RestResponse.ok(systemInformationService.executeDynamicQuery(dto));
          } 

    2. service 实现
    3. // 主方法
       public Object executeDynamicQuery(HashMap dto) {
              String method = (String) dto.get(TworkConstants.QUERY_METHOD);
              dto.put(TworkConstants.QUERY, dto.clone());
      
              // 1. 获取标签xml,根据入参的funCode
              String xmlSql = informationMapper.queryDynamicSqlByMethodCode(method);
              if (StringUtils.isEmpty(xmlSql)) {
                  ExceptionUtil.error("未查询到当前动态sql模板!");
              }
      
              // 2. 调用mybatis的xml-sql解析方法,将等 mybatis标签解析替换
              String parseSql = this.parseMybatisTags(xmlSql, dto);
              log.info("================"+ parseSql);
              dto.put(TworkConstants.SQL, parseSql);
      
              Object responseType = dto.get(TworkConstants.QUERY_TYPE);
      
              // 3. 根据响应类型,返回不同格式的json结构
              if (TworkConstants.QUERY_TYPE_PAGE.equals(responseType)) {
                  Integer current = (Integer) dto.get(TworkConstants.PAGE_NO);
                  Integer size = (Integer) dto.get(TworkConstants.LIMIT);
                  Page> pageParam = new Page<>(current, size);
      
                  return informationMapper.executePageDynamicQuery(pageParam, dto);
              } else if (TworkConstants.QUERY_TYPE_ONE.equals(responseType)) {
                  return informationMapper.executeOneDynamicQuery(dto);
              } else {
                  return informationMapper.executeListDynamicQuery(dto);
              }
          }
      
          /**
           * 格式化标签模板的参数
           * @param xmlSql
           * @param dto
           * @return
           */
          private String parseMybatisTags(String xmlSql, HashMap dto) {
              String sql = "";
              // 实例化解析 XML对象
              XPathParser parser = new XPathParser(sql, false, null, new XMLMapperEntityResolver());
              XNode context = parser.evalNode("/select");
      
              Configuration configuration = new Configuration();
              configuration.setDatabaseId("");
              TworkXMLScriptBuilder xmlScriptBuilder = new TworkXMLScriptBuilder(configuration, context);
      
              TworkDynamicSqlSource sqlSource = xmlScriptBuilder.parseTworkScriptNode();
              return sqlSource.getTworkParseSql(dto);
          }

                               

          /** 动态模板查询-常量值 **/
          String QUERY_METHOD = "queryMethod";
      
          String QUERY = "query";
      
          String SQL= "sql";
          String QUERY_TYPE_PAGE = "page";
          String QUERY_TYPE_LIST = "list";
          String QUERY_TYPE_ONE = "one";
      
          String QUERY_TYPE = "queryType";
          String PAGE_NO = "pageNo";
          String LIMIT = "limit";
      // 相关的查询 xml模板
          
          
          
          

          3.  2个类(TworkXMLScriptBuilder、TworkDynamicSqlSource)

              

      package cn.xxx.xx.twork.controller.info;
      
      import org.apache.ibatis.builder.SqlSourceBuilder;
      import org.apache.ibatis.mapping.BoundSql;
      import org.apache.ibatis.mapping.SqlSource;
      import org.apache.ibatis.scripting.xmltags.DynamicContext;
      import org.apache.ibatis.scripting.xmltags.SqlNode;
      import org.apache.ibatis.session.Configuration;
      
      public class TworkDynamicSqlSource implements SqlSource {
      
          private final Configuration configuration;
          private final SqlNode rootSqlNode;
      
          public TworkDynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
              this.configuration = configuration;
              this.rootSqlNode = rootSqlNode;
          }
      
          @Override
          public BoundSql getBoundSql(Object parameterObject) {
              DynamicContext context = new DynamicContext(configuration, parameterObject);
              rootSqlNode.apply(context);
              // 执行完上面的方法,在这里可以直接获取到解析后带#{} ${}的sql
              SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
              Class parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
              SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
          
              // 执行完上面的方法,就会#{} ${} 给替换成jdbc的问号
              BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
              context.getBindings().forEach(boundSql::setAdditionalParameter);
              return boundSql;
          }
      
          public String getTworkParseSql(Object parameterObject) {
              DynamicContext context = new DynamicContext(configuration, parameterObject);
              rootSqlNode.apply(context);
              
              // 在这里可以直接获取到解析后带#{} ${}的sql
              return context.getSql();
          }
      
      }
      
      

      package cn.xxx.xx.twork.controller.info;
      
      import org.apache.ibatis.builder.BaseBuilder;
      import org.apache.ibatis.builder.BuilderException;
      import org.apache.ibatis.mapping.SqlSource;
      import org.apache.ibatis.parsing.XNode;
      import org.apache.ibatis.scripting.defaults.RawSqlSource;
      import org.apache.ibatis.scripting.xmltags.*;
      import org.apache.ibatis.session.Configuration;
      import org.w3c.dom.Node;
      import org.w3c.dom.NodeList;
      
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      
      public class TworkXMLScriptBuilder  extends BaseBuilder {
      
      
          private final XNode context;
          private boolean isDynamic;
          private final Class parameterType;
          private final Map nodeHandlerMap = new HashMap<>();
      
          public TworkXMLScriptBuilder(Configuration configuration, XNode context) {
              this(configuration, context, null);
          }
      
          public TworkXMLScriptBuilder(Configuration configuration, XNode context, Class parameterType) {
              super(configuration);
              this.context = context;
              this.parameterType = parameterType;
              initNodeHandlerMap();
          }
      
      
          private void initNodeHandlerMap() {
              nodeHandlerMap.put("trim", new TrimHandler());
              nodeHandlerMap.put("where", new WhereHandler());
              nodeHandlerMap.put("set", new SetHandler());
              nodeHandlerMap.put("foreach", new ForEachHandler());
              nodeHandlerMap.put("if", new IfHandler());
              nodeHandlerMap.put("choose", new ChooseHandler());
              nodeHandlerMap.put("when", new IfHandler());
              nodeHandlerMap.put("otherwise", new OtherwiseHandler());
              nodeHandlerMap.put("bind", new BindHandler());
          }
      
          public SqlSource parseScriptNode() {
              MixedSqlNode rootSqlNode = parseDynamicTags(context);
              SqlSource sqlSource;
              if (isDynamic) {
                  sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
                  // 注释掉mybatis自带的,返回我们自己的sqlSource
      //            sqlSource = new TworkDynamicSqlSource(configuration, rootSqlNode);
              } else {
                  sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
              }
              return sqlSource;
          }
      
          public TworkDynamicSqlSource parseTworkScriptNode() {
              // 注释掉mybatis自带的,返回我们自己的sqlSource
              MixedSqlNode rootSqlNode = parseDynamicTags(context);
      
              return new TworkDynamicSqlSource(configuration, rootSqlNode);
          }
      
          protected MixedSqlNode parseDynamicTags(XNode node) {
              List contents = new ArrayList<>();
              NodeList children = node.getNode().getChildNodes();
              for (int i = 0; i < children.getLength(); i++) {
                  XNode child = node.newXNode(children.item(i));
                  if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
                      String data = child.getStringBody("");
                      TextSqlNode textSqlNode = new TextSqlNode(data);
                      if (textSqlNode.isDynamic()) {
                          contents.add(textSqlNode);
                          isDynamic = true;
                      } else {
                          contents.add(new StaticTextSqlNode(data));
                      }
                  } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
                      String nodeName = child.getNode().getNodeName();
                      NodeHandler handler = nodeHandlerMap.get(nodeName);
                      if (handler == null) {
                          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
                      }
                      handler.handleNode(child, contents);
                      isDynamic = true;
                  }
              }
              return new MixedSqlNode(contents);
          }
      
          private interface NodeHandler {
              void handleNode(XNode nodeToHandle, List targetContents);
          }
      
          private class BindHandler implements NodeHandler {
              public BindHandler() {
                  // Prevent Synthetic Access
              }
      
              @Override
              public void handleNode(XNode nodeToHandle, List targetContents) {
                  final String name = nodeToHandle.getStringAttribute("name");
                  final String expression = nodeToHandle.getStringAttribute("value");
                  final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
                  targetContents.add(node);
              }
          }
      
          private class TrimHandler implements NodeHandler {
              public TrimHandler() {
                  // Prevent Synthetic Access
              }
      
              @Override
              public void handleNode(XNode nodeToHandle, List targetContents) {
                  MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                  String prefix = nodeToHandle.getStringAttribute("prefix");
                  String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
                  String suffix = nodeToHandle.getStringAttribute("suffix");
                  String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
                  TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
                  targetContents.add(trim);
              }
          }
      
          private class WhereHandler implements NodeHandler {
              public WhereHandler() {
                  // Prevent Synthetic Access
              }
      
              @Override
              public void handleNode(XNode nodeToHandle, List targetContents) {
                  MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                  WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
                  targetContents.add(where);
              }
          }
      
          private class SetHandler implements NodeHandler {
              public SetHandler() {
                  // Prevent Synthetic Access
              }
      
              @Override
              public void handleNode(XNode nodeToHandle, List targetContents) {
                  MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                  SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
                  targetContents.add(set);
              }
          }
      
          private class ForEachHandler implements NodeHandler {
              public ForEachHandler() {
                  // Prevent Synthetic Access
              }
      
              @Override
              public void handleNode(XNode nodeToHandle, List targetContents) {
                  MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                  String collection = nodeToHandle.getStringAttribute("collection");
                  String item = nodeToHandle.getStringAttribute("item");
                  String index = nodeToHandle.getStringAttribute("index");
                  String open = nodeToHandle.getStringAttribute("open");
                  String close = nodeToHandle.getStringAttribute("close");
                  String separator = nodeToHandle.getStringAttribute("separator");
                  ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
                  targetContents.add(forEachSqlNode);
              }
          }
      
          private class IfHandler implements NodeHandler {
              public IfHandler() {
                  // Prevent Synthetic Access
              }
      
              @Override
              public void handleNode(XNode nodeToHandle, List targetContents) {
                  MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                  String test = nodeToHandle.getStringAttribute("test");
                  IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
                  targetContents.add(ifSqlNode);
              }
          }
      
          private class OtherwiseHandler implements NodeHandler {
              public OtherwiseHandler() {
                  // Prevent Synthetic Access
              }
      
              @Override
              public void handleNode(XNode nodeToHandle, List targetContents) {
                  MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                  targetContents.add(mixedSqlNode);
              }
          }
      
          private class ChooseHandler implements NodeHandler {
              public ChooseHandler() {
                  // Prevent Synthetic Access
              }
      
              @Override
              public void handleNode(XNode nodeToHandle, List targetContents) {
                  List whenSqlNodes = new ArrayList<>();
                  List otherwiseSqlNodes = new ArrayList<>();
                  handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
                  SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
                  ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
                  targetContents.add(chooseSqlNode);
              }
      
              private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List ifSqlNodes, List defaultSqlNodes) {
                  List children = chooseSqlNode.getChildren();
                  for (XNode child : children) {
                      String nodeName = child.getNode().getNodeName();
                      NodeHandler handler = nodeHandlerMap.get(nodeName);
                      if (handler instanceof IfHandler) {
                          handler.handleNode(child, ifSqlNodes);
                      } else if (handler instanceof OtherwiseHandler) {
                          handler.handleNode(child, defaultSqlNodes);
                      }
                  }
              }
      
              private SqlNode getDefaultSqlNode(List defaultSqlNodes) {
                  SqlNode defaultSqlNode = null;
                  if (defaultSqlNodes.size() == 1) {
                      defaultSqlNode = defaultSqlNodes.get(0);
                  } else if (defaultSqlNodes.size() > 1) {
                      throw new BuilderException("Too many default (otherwise) elements in choose statement.");
                  }
                  return defaultSqlNode;
              }
          }
      }
      

      3. 使用方法

      存储mybatis的xml标签,动态sql 查询_第2张图片

      你可能感兴趣的:(小熊猫的笔记本,mybatis,sql,xml)