当前位置: 首页 > news >正文

tk.mybatis多层括号嵌套SQL查询

tk.mybatis多层括号嵌套SQL查询

  • 现状
  • 解决思路
  • 具体实现
    • 1、扩展`tk.mybatis.mapper.entity.Example`实现父子节点
    • 2、将父子层次结构转为单层循环接口
      • 定义枚举`XyzSqlNodeType `
      • 定义`SQL`节点类
      • 层接结构转换为水平结构
      • 在`XyzExample`中增加`SQL`节点
      • 判断结构转换是否正确,输出SQL语句
      • 单元测试一下
    • 3、将`nodes`在XML文件中体现
      • 新建一个Mapper
  • 实际使用
    • 定义实际Mapper,扩展自`BaseXyzExampleMapper`

这里考虑的是单表复杂逻辑查询,多层括号嵌套

现状

tk.mybatis.mapper.provider.ExampleProvider#selectByExample
这里是selectByExample底层SQL操作的方法

  public String selectByExample(MappedStatement ms) {Class<?> entityClass = getEntityClass(ms);//将返回值修改为实体类型setResultType(ms, entityClass);StringBuilder sql = new StringBuilder("SELECT ");if (isCheckExampleEntityClass()) {sql.append(SqlHelper.exampleCheck(entityClass));}sql.append("<if test=\"distinct\">distinct</if>");//支持查询指定列sql.append(SqlHelper.exampleSelectColumns(entityClass));sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));sql.append(SqlHelper.exampleWhereClause());sql.append(SqlHelper.exampleOrderBy(entityClass));sql.append(SqlHelper.exampleForUpdate());return sql.toString();}

tk.mybatis.mapper.mapperhelper.SqlHelper#exampleWhereClause
这里是where条件拼接内容

  public static String exampleWhereClause() {return "<if test=\"_parameter != null\">" +"<where>\n" +" ${@tk.mybatis.mapper.util.OGNL@andNotLogicDelete(_parameter)}" +" <trim prefix=\"(\" prefixOverrides=\"and |or \" suffix=\")\">\n" +"  <foreach collection=\"oredCriteria\" item=\"criteria\">\n" +"    <if test=\"criteria.valid\">\n" +"      ${@tk.mybatis.mapper.util.OGNL@andOr(criteria)}" +"      <trim prefix=\"(\" prefixOverrides=\"and |or \" suffix=\")\">\n" +"        <foreach collection=\"criteria.criteria\" item=\"criterion\">\n" +"          <choose>\n" +"            <when test=\"criterion.noValue\">\n" +"              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +"            </when>\n" +"            <when test=\"criterion.singleValue\">\n" +"              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value}\n" +"            </when>\n" +"            <when test=\"criterion.betweenValue\">\n" +"              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" +"            </when>\n" +"            <when test=\"criterion.listValue\">\n" +"              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +"              <foreach close=\")\" collection=\"criterion.value\" item=\"listItem\" open=\"(\" separator=\",\">\n" +"                #{listItem}\n" +"              </foreach>\n" +"            </when>\n" +"          </choose>\n" +"        </foreach>\n" +"      </trim>\n" +"    </if>\n" +"  </foreach>\n" +" </trim>\n" +"</where>" +"</if>";}

总结: 从源代码查看,复杂括号逻辑的SQL是不支持的;而且在xml文件中通过include来实现嵌套逻辑会导致死递归

解决思路

1、一层循环,把每一个括号、AND、OR、条件作为一个节点循环输出
2、扩展tk.mybatis,能用的轮子继续用

  • 输入
(type = '4')
AND ((((status = '1'and user_id = 1)))OR (((status = '2'and user_id = 2)))
)
  • 输出
List<String> list = Arrays.asList("(", "type='4'", ")", " AND ");# xml中循环
<foreach item="item" collection="items" index="index"><choose><when test="左括号">(</when><when test="右括号">)</when><when test="AND符号"> AND </when><when test="OR符号"> OR </when><when test="条件">${name} = #{value}</when></choose><otherwise></otherwise>
<foreach>

具体实现

1、扩展tk.mybatis.mapper.entity.Example实现父子节点

这里自定了层级结构的groupCriteriaList字段,放弃原Example中的oredCriteria字段

package org.x.y.z.ext;import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.entity.EntityTable;
import tk.mybatis.mapper.mapperhelper.EntityHelper;import java.util.*;
import java.util.stream.Collectors;public class XyzExample<T> extends tk.mybatis.mapper.entity.Example {public XyzExample(Class<T> entityClass) {super(entityClass);this.tableName = getEntityTable(entityClass);}/*** 表名*/protected String tableName;public String getTableName() {return tableName;}/*** 层级SQL*/protected List<XyzExample.GroupCriteria> groupCriteriaList = new ArrayList<XyzExample.GroupCriteria>();protected String getEntityTable(Class<?> entityClass) {EntityTable entityTable = EntityHelper.getEntityTable(entityClass);return entityTable.getName();}public List<String> getAllColumns() {Set<EntityColumn> columnSet = EntityHelper.getColumns(entityClass);if (CollectionUtils.isEmpty(columnSet)) {return new ArrayList<String>();}return columnSet.stream().filter(Objects::nonNull).map(EntityColumn::getColumn).collect(Collectors.toList());}public GroupCriteria createGroupCriteria(boolean isAnd) {GroupCriteria groupCriteria = new GroupCriteria();groupCriteria.setAndOr(isAnd ? " AND " : " OR ");groupCriteriaList.add(groupCriteria);return groupCriteria;}protected Criteria createCriteriaInner(boolean isAnd) {Criteria criteria = createCriteriaInternal();criteria.setAndOr(isAnd ? " AND " : " OR ");return criteria;}@Datapublic class GroupCriteria {private String andOr;private List<Criteria> criteriaList = new ArrayList<Criteria>();private List<GroupCriteria> subGroupCriteriaList = new ArrayList<GroupCriteria>();public GroupCriteria createSubGroupCriteria(boolean isAnd) {GroupCriteria groupCriteria = new GroupCriteria();groupCriteria.setAndOr(isAnd ? " AND " : " OR ");subGroupCriteriaList.add(groupCriteria);return groupCriteria;}public Criteria createCriteria(boolean isAnd) {Criteria criteria = createCriteriaInner(isAnd);criteriaList.add(criteria);return criteria;}}
}    

实际使用:

XyzExample<OrderDO> example = new XyzExample<OrderDO>(OrderDO.class);
example.orderBy(OrderDO.Fields.id).desc();XyzExample.GroupCriteria group1 = example.createGroupCriteria(true);
group1.createCriteria(true).andEqualTo(OrderDO.Fields.type, 1);XyzExample.GroupCriteria group11 = group1.createSubGroupCriteria(true);
group11.createCriteria(true).andEqualTo(OrderDO.Fields.status, 1).andEqualTo(OrderDO.Fields.userId, 1);XyzExample.GroupCriteria group12 = group1.createSubGroupCriteria(false);
group12.createCriteria(true).andEqualTo(OrderDO.Fields.status, 2).andEqualTo(OrderDO.Fields.userId, 2);XyzExample.GroupCriteria group2 = example.createGroupCriteria(false);
group2.createCriteria(true).andEqualTo(OrderDO.Fields.type, 2);XyzExample.GroupCriteria group21 = group2.createSubGroupCriteria(true);
group21.createCriteria(true).andEqualTo(OrderDO.Fields.status, 3).andEqualTo(OrderDO.Fields.userId, 3);XyzExample.GroupCriteria group22 = group2.createSubGroupCriteria(false);
group22.createCriteria(true).andEqualTo(OrderDO.Fields.status, 4).andEqualTo(OrderDO.Fields.userId, 4);
List<OrderDO> list = orderMapper.selectByExampleExtend(example);

2、将父子层次结构转为单层循环接口

定义枚举XyzSqlNodeType

public enum XyzSqlNodeType {/*** 左括号: (*/LEFT_PARENTHESE,/*** 右括号: )*/RIGHT_PARENTHESE,/*** AND、OR*/ANDOR,/*** 条件: a = 1*/CONDITION
}

定义SQL节点类

@Accessors(chain = true)
@Data
public class XyzSqlNode {/*** 节点类型*/private XyzSqlNodeType type;/*** "AND", "OR"(用于连接符)*/private String andOr;/*** 字段值1*/private Object value;/*** 字段值2*/private Object secondValue;/*** 例如: a =*/private String condition;/*** 例如: a IS NULL*/private boolean noValue;/*** 例如: a = 1*/private boolean singleValue;/*** 例如: a between 1 and 100*/private boolean betweenValue;/*** 例如: a IN(1,2,3)*/private boolean listValue;
}

层接结构转换为水平结构

public class XyzSqlUtil {public static List<XyzSqlNode> getSqlNodeListByGroup(List<XyzExample.GroupCriteria> groupCriteriaList) {List<XyzSqlNode> list = new ArrayList<XyzSqlNode>();if (CollectionUtils.isEmpty(groupCriteriaList)) {return list;}int i = 0;for (XyzExample.GroupCriteria it : groupCriteriaList) {if (Objects.isNull(it)) {continue;}if (i > 0) {list.add(new XyzSqlNode().setType(XyzSqlNodeType.ANDOR).setAndOr(it.getAndOr()));}List<XyzSqlNode> subNodeList = getSqlNodeListByGroup(it);if (CollectionUtils.isNotEmpty(subNodeList)) {list.addAll(subNodeList);i++;}}return list;}private static List<XyzSqlNode> getSqlNodeListByGroup(XyzExample.GroupCriteria groupCriteria) {List<XyzSqlNode> list = new ArrayList<XyzSqlNode>();if (Objects.isNull(groupCriteria)) {return list;}List<XyzSqlNode> nodeList = getSqlNodeList(groupCriteria.getCriteriaList());if (CollectionUtils.isNotEmpty(groupCriteria.getSubGroupCriteriaList())) {// 递归List<XyzSqlNode> subList = getSqlNodeListByGroup(groupCriteria.getSubGroupCriteriaList());if (CollectionUtils.isNotEmpty(nodeList)) {list.add(new XyzSqlNode().setType(XyzSqlNodeType.LEFT_PARENTHESE));list.addAll(nodeList);if (CollectionUtils.isNotEmpty(subList)) {list.add(new XyzSqlNode().setType(XyzSqlNodeType.ANDOR).setAndOr(groupCriteria.getAndOr()));list.add(new XyzSqlNode().setType(XyzSqlNodeType.LEFT_PARENTHESE));list.addAll(subList);list.add(new XyzSqlNode().setType(XyzSqlNodeType.RIGHT_PARENTHESE));}list.add(new XyzSqlNode().setType(XyzSqlNodeType.RIGHT_PARENTHESE));} else {if (CollectionUtils.isNotEmpty(subList)) {list.add(new XyzSqlNode().setType(XyzSqlNodeType.LEFT_PARENTHESE));list.addAll(subList);list.add(new XyzSqlNode().setType(XyzSqlNodeType.RIGHT_PARENTHESE));}}} else {if (CollectionUtils.isNotEmpty(nodeList)) {list.add(new XyzSqlNode().setType(XyzSqlNodeType.LEFT_PARENTHESE));list.addAll(nodeList);list.add(new XyzSqlNode().setType(XyzSqlNodeType.RIGHT_PARENTHESE));}}return list;}private static List<XyzSqlNode> getSqlNodeList(List<Example.Criteria> criteriaList) {ArrayList<XyzSqlNode> list = new ArrayList<XyzSqlNode>();if (CollectionUtils.isEmpty(criteriaList)) {return list;}list.add(new XyzSqlNode().setType(XyzSqlNodeType.LEFT_PARENTHESE));int i = 0;for (Example.Criteria it : criteriaList) {if (Objects.isNull(it)) {continue;}if (i > 0) {list.add(new XyzSqlNode().setType(XyzSqlNodeType.ANDOR).setAndOr(it.getAndOr()));}List<XyzSqlNode> subNodeList = getSqlNodeList(it);if (CollectionUtils.isNotEmpty(subNodeList)) {list.addAll(subNodeList);i++;}}if (CollectionUtils.size(list) == 1) {list.clear();} else {list.add(new XyzSqlNode().setType(XyzSqlNodeType.RIGHT_PARENTHESE));}return list;}private static List<XyzSqlNode> getSqlNodeList(Example.Criteria criteria) {ArrayList<XyzSqlNode> nodeList = new ArrayList<XyzSqlNode>();if (Objects.isNull(criteria) || CollectionUtils.isEmpty(criteria.getCriteria())) {return nodeList;}nodeList.add(new XyzSqlNode().setType(XyzSqlNodeType.LEFT_PARENTHESE));int i = 0;for (Example.Criterion it : criteria.getCriteria()) {if (Objects.isNull(it)) {continue;}if (i > 0) {nodeList.add(new XyzSqlNode().setType(XyzSqlNodeType.ANDOR).setAndOr(" " + it.getAndOr() + " "));}XyzSqlNode subXyzSqlNode = new XyzSqlNode();subXyzSqlNode.setType(XyzSqlNodeType.CONDITION);subXyzSqlNode.setCondition(it.getCondition());subXyzSqlNode.setValue(it.getValue());subXyzSqlNode.setSecondValue(it.getSecondValue());subXyzSqlNode.setNoValue(it.isNoValue());subXyzSqlNode.setSingleValue(it.isSingleValue());subXyzSqlNode.setBetweenValue(it.isBetweenValue());subXyzSqlNode.setListValue(it.isListValue());nodeList.add(subXyzSqlNode);i++;}nodeList.add(new XyzSqlNode().setType(XyzSqlNodeType.RIGHT_PARENTHESE));return nodeList;}
}

XyzExample中增加SQL节点

public class XyzExample<T> extends tk.mybatis.mapper.entity.Example {// 其他代码protected List<XyzSqlNode> nodes = new ArrayList<XyzSqlNode>();public List<XyzSqlNode> getNodes() {if (CollectionUtils.isNotEmpty(nodes)) {return nodes;}nodes = XyzSqlUtil.getSqlNodeListByGroup(groupCriteriaList);return nodes;}
}

判断结构转换是否正确,输出SQL语句

public class XyzSqlUtil {public static String parseWhere(List<XyzSqlNode> nodes) {if (CollectionUtils.isEmpty(nodes)) {return null;}StringBuilder builder = new StringBuilder();for (XyzSqlNode node : nodes) {switch (node.getType()) {case LEFT_PARENTHESE:builder.append("(");break;case RIGHT_PARENTHESE:builder.append(")");break;case ANDOR:builder.append(node.getAndOr());break;case CONDITION:if (node.isNoValue()) {builder.append(node.getCondition());} else if (node.isSingleValue()) {if (node.getValue() instanceof Number) {builder.append(node.getCondition()).append(node.getValue());} else {builder.append(node.getCondition()).append("'").append(node.getValue()).append("'");}} else if (node.isBetweenValue()) {builder.append(node.getCondition()).append(" AND ");if (node.getValue() instanceof Number) {builder.append(node.getValue());} else {builder.append("'").append(node.getValue()).append("'");}} else if (node.isListValue()) {builder.append(node.getCondition()).append(" (");Collection<Object> coll = (Collection<Object>) (node.getValue());int i = 0;for (Object o : coll) {if (Objects.isNull(o)) {continue;}if (i > 0) {builder.append(",");}if (o instanceof Number) {builder.append(o);} else {builder.append("'").append(o).append("'");}i++;}builder.append(")");}break;default:break;}}return builder.toString();}
}

单元测试一下

@Test
public void test1() {EntityHelper.initEntityNameMap(OrderDO.class, new Config());XyzExample<OrderDO> example = new XyzExample<OrderDO>(OrderDO.class);example.orderBy(OrderDO.Fields.id).desc();XyzExample.GroupCriteria group1 = example.createGroupCriteria(true);group1.createCriteria(true).andEqualTo(OrderDO.Fields.type, 1);XyzExample.GroupCriteria group11 = group1.createSubGroupCriteria(true);group11.createCriteria(true).andEqualTo(OrderDO.Fields.status, 1).andEqualTo(OrderDO.Fields.userId, 1);XyzExample.GroupCriteria group12 = group1.createSubGroupCriteria(false);group12.createCriteria(true).andEqualTo(OrderDO.Fields.status, 2).andEqualTo(OrderDO.Fields.userId, 2);XyzExample.GroupCriteria group2 = example.createGroupCriteria(false);group2.createCriteria(true).andEqualTo(OrderDO.Fields.type, 2);XyzExample.GroupCriteria group21 = group2.createSubGroupCriteria(true);group21.createCriteria(true).andEqualTo(OrderDO.Fields.status, 3).andEqualTo(OrderDO.Fields.userId, 3);XyzExample.GroupCriteria group22 = group2.createSubGroupCriteria(false);group22.createCriteria(true).andEqualTo(OrderDO.Fields.status, 4).andEqualTo(OrderDO.Fields.userId, 4);System.out.println(XyzSqlUtil.parseWhere(example.getNodes()));
}

3、将nodes在XML文件中体现

新建一个Mapper

public interface BaseXyzExampleMapper {// 内部什么都不需要
}

对应XML文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.x.y.z.ext.BaseXyzExampleMapper"><sql id="_selectByExampleExtend">SELECT<if test="param.distinct">distinct</if><include refid="org.x.y.z.ext.BaseXyzExampleMapper._getExampleExtendColumn"/>FROM ${param.tableName}<include refid="org.x.y.z.ext.BaseXyzExampleMapper._getExampleExtendWhere"/><if test="param.orderByClause != null">ORDER BY ${param.orderByClause}</if></sql><sql id="_getExampleExtendColumn"><choose><when test="param.selectColumns != null and !param.selectColumns.isEmpty()"><foreach collection="param.selectColumns" item="column" open="" close="" separator=",">${column}</foreach></when><otherwise><foreach collection="param.getAllColumns()" item="column" open="" close="" separator=",">${column}</foreach></otherwise></choose></sql><sql id="_getExampleExtendWhere"><if test="param.nodes != null and !param.nodes.isEmpty()"><where><trim prefixOverrides="AND |OR "><foreach item="node" collection="param.nodes" index="index"><choose><!-- 处理开括号 --><when test="node.type !=null and node.type == @org.x.y.z.ext.XyzSqlNodeType@LEFT_PARENTHESE">(</when><!-- 处理闭括号 --><when test="node.type !=null and node.type == @org.x.y.z.ext.XyzSqlNodeType@RIGHT_PARENTHESE">)</when><!-- 处理 AND OR --><when test="node.type !=null and node.type == @org.x.y.z.ext.XyzSqlNodeType@ANDOR">${index > 0 ? node.andOr : ''}</when><!-- 处理条件 --><when test="node.type !=null and node.type == @org.x.y.z.ext.XyzSqlNodeType@CONDITION"><choose><when test="node.noValue">${node.condition}</when><when test="node.singleValue">${node.condition} #{node.value}</when><when test="node.betweenValue">${node.condition} #{node.value} AND #{node.secondValue}</when><when test="node.listValue">${node.condition}<foreach collection="node.value" item="listItem" open="(" close=")" separator=",">#{listItem}</foreach></when></choose></when><otherwise></otherwise></choose></foreach></trim></where></if></sql>
</mapper>

实际使用

定义实际Mapper,扩展自BaseXyzExampleMapper

@org.apache.ibatis.annotations.Mapper
public interface OrderMapper extends tk.mybatis.mapper.common.Mapper<OrderDO>, InsertListMapper<OrderDO>, BaseXyzExampleMapper {// 定义查询方法List<OrderDO> selectByExampleExtend(@Param("param") XyzExample<OrderDO> param);
}

对应XML:

    <select id="selectByExampleExtend" resultType="org.x.y.z.entity.OrderDO"><include refid="org.x.y.z.ext.BaseXyzExampleMapper._selectByExampleExtend"/></select>
http://www.dtcms.com/a/273087.html

相关文章:

  • 本地部署文档管理系统 Paperless-ngx 并实现外部访问
  • 腾讯云分为几个区域
  • K线连续涨跌统计与分析工具
  • C++的类中的虚拟继承【底层剖析(配图解)】
  • Java多线程:核心技术与实战指南
  • 鸿蒙智行6月交付新车52747辆 单日交付量3651辆
  • 如何设计一个登录管理系统:单点登录系统架构设计
  • 无法识别的USB设备怎么解决 一键修复
  • JAVA JVM对象的实现
  • [2025CVPR]CCFS:高IPC数据集蒸馏的课程式粗细筛选技术解析
  • OkHttp 的拦截器有哪些
  • 苍穹外卖—day1
  • 树莓派5+Ubuntu24.04 LTS ROS2 N10P镭神激光雷达 保姆级教程
  • Linux Ubuntu 安装 AnythingLLM
  • STM32中DMA(直接存储器访问)详解
  • [Meetily后端框架] AI摘要结构化 | `SummaryResponse`模型 | Pydantic库 | vs marshmallow库
  • Spring Boot 与 Docker 的完美结合:容器化你的应用
  • 时序数据库InfluxDB
  • Flink 2.0 DataStream算子全景
  • MBSE工具+架构建模:从效率提升到质量赋能
  • 智能Agent场景实战指南 Day 9:市场营销Agent构建策略
  • 粗排样本架构升级:融合LTR特征提升模型性能的技术实践
  • 车载诊断架构 --- DTC深层次参数信息(e.g. ComfirmDTCLimit unconfirmDTCLimit)
  • 第10章 语句 笔记
  • 轻松使用格式工厂中的分离器功能来分离视频和音频文件
  • 噪音到10µVRMS 以下的DC-DC:TPS62913
  • 实现一个点击输入框可以弹出的数字软键盘控件 qt 5.12
  • Java 单例类详解:从基础到高级,掌握线程安全与高效设计
  • wpf使用webview2显示网页内容(最低兼容.net framework4.5.2)
  • C Primer Plus 第6版 编程练习——第8章