【MyBatis源码】SQL 语句构建器AbstractSQL

文章目录

  • 介绍
  • org.apache.ibatis.jdbc.SQL
  • SQL类使用示例
  • @SelectProvider搭配动态SQL
  • AbstractSQL类源码分析

介绍

当我们需要使用Statement对象执行SQL时,SQL语句会嵌入Java代码中。SQL语句比较复杂时,我们可能会在代码中对SQL语句进行拼接,查询条件不固定时,还需要根据不同条件拼接不同的SQL语句。在MyBatis中已经为我们提供了这类开发工具类。
MyBatis 中的 AbstractSQL 类是 MyBatis 提供的一个用于构建动态 SQL 语句的工具类。它的主要用途是帮助开发者更灵活、优雅地拼接复杂的 SQL 语句,尤其是在需要根据条件生成不同的 SQL 语句时,可以减少手动拼接 SQL 字符串的繁琐工作,并提高代码的可读性和维护性。
在日常开发中,尤其是当涉及到复杂查询、更新、插入时,AbstractSQL 可以用于生成动态 SQL。其用途主要包括以下几个方面:

动态条件查询:根据不同的条件拼接 SELECT 语句。
动态插入:根据传入的实体类生成不同的 INSERT 语句。
动态更新:根据条件生成 UPDATE 语句,只更新有值的字段。
动态删除:根据条件拼接 DELETE 语句。
AbstractSQL 的核心思想是通过链式调用的方式构建 SQL 语句,这类似于构建器模式(Builder Pattern)。

org.apache.ibatis.jdbc.SQL

public class SQL extends AbstractSQL<SQL> {@Overridepublic SQL getSelf() {return this;}
}

SQL类继承了AbstractSQL,在日常开发中一般使用SQL类。

SQL类使用示例

【动态构建 SELECT 查询】

  @Testpublic void testSelectSQL() {String orgSql = "SELECT P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME, P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON\n" +"FROM PERSON P, ACCOUNT A\n" +"INNER JOIN DEPARTMENT D on D.ID = P.DEPARTMENT_ID\n" +"INNER JOIN COMPANY C on D.COMPANY_ID = C.ID\n" +"WHERE (P.ID = A.ID AND P.FIRST_NAME like ?) \n" +"OR (P.LAST_NAME like ?)\n" +"GROUP BY P.ID\n" +"HAVING (P.LAST_NAME like ?) \n" +"OR (P.FIRST_NAME like ?)\n" +"ORDER BY P.ID, P.FULL_NAME";String newSql = new SQL() {{SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");FROM("PERSON P");FROM("ACCOUNT A");INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");WHERE("P.ID = A.ID");WHERE("P.FIRST_NAME like ?");OR();WHERE("P.LAST_NAME like ?");GROUP_BY("P.ID");HAVING("P.LAST_NAME like ?");OR();HAVING("P.FIRST_NAME like ?");ORDER_BY("P.ID");ORDER_BY("P.FULL_NAME");}}.toString();assertEquals(orgSql, newSql);}
// 匿名内部类风格
public String deletePersonSql() {return new SQL() {{DELETE_FROM("PERSON");WHERE("ID = #{id}");}}.toString();
}// Builder / Fluent 风格
public String insertPersonSql() {String sql = new SQL().INSERT_INTO("PERSON").VALUES("ID, FIRST_NAME", "#{id}, #{firstName}").VALUES("LAST_NAME", "#{lastName}").toString();return sql;
}// 动态条件(注意参数需要使用 final 修饰,以便匿名内部类对它们进行访问)
public String selectPersonLike(final String id, final String firstName, final String lastName) {return new SQL() {{SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");FROM("PERSON P");if (id != null) {WHERE("P.ID like #{id}");}if (firstName != null) {WHERE("P.FIRST_NAME like #{firstName}");}if (lastName != null) {WHERE("P.LAST_NAME like #{lastName}");}ORDER_BY("P.LAST_NAME");}}.toString();
}public String deletePersonSql() {return new SQL() {{DELETE_FROM("PERSON");WHERE("ID = #{id}");}}.toString();
}public String insertPersonSql() {return new SQL() {{INSERT_INTO("PERSON");VALUES("ID, FIRST_NAME", "#{id}, #{firstName}");VALUES("LAST_NAME", "#{lastName}");}}.toString();
}public String updatePersonSql() {return new SQL() {{UPDATE("PERSON");SET("FIRST_NAME = #{firstName}");WHERE("ID = #{id}");}}.toString();
}

相关官方文档:
https://mybatis.org/mybatis-3/zh_CN/statement-builders.html

@SelectProvider搭配动态SQL

@SelectProvider 注解是 MyBatis 提供的一种动态 SQL 语句生成方式,用于将复杂的查询逻辑封装在 Java 方法中,而不是直接在 Mapper 接口的方法上书写固定的 SQL 语句。通过 @SelectProvider 注解,MyBatis 可以根据实际情况动态生成 SQL,这使得 SQL 语句更加灵活和可维护。
@SelectProvider 的基本语法

@SelectProvider(type = SQLProviderClass.class, method = "methodName")
List<ResultType> selectMethod(参数);

type:指定提供 SQL 语句的类(通常称为 Provider 类)。
method:指定 Provider 类中的方法名称,该方法用于动态生成 SQL。
selectMethod:Mapper 接口中的方法,最终会执行 methodName 返回的 SQL 语句。

这个注解和@SELECT的区别在于@SelectProvider 注解可以中参数SQLProviderClass可以搭配SQL动态语句类关联SQL。

  @SelectProvider(type = UserSqlProvider.class, method = "buildSelectSql")List<Map<String, Object>> selectUsers(Map<String, Object> params);
public class UserSqlProvider {public String buildSelectSql(Map<String, Object> params) {return new SQL() {{SELECT("*");FROM("t_user");if (params.get("name") != null) {WHERE("name = #{name}");}if (params.get("age") != null) {WHERE("age = #{age}");}ORDER_BY("id DESC");}}.toString();}
}

测试类:

  /*** 测试@SelectProvider使用*/@Testpublic void test6() throws Exception {InputStream resource = Resources.getResourceAsStream(MybatisTest.class.getClassLoader(), "mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);Configuration configuration = sqlSessionFactory.getConfiguration();// 手动注册mapperconfiguration.addMapper(UserMapper.class);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);Map<String, Object> params = new HashMap<>();params.put("name", "zhangSan");params.put("age", 18);List<Map<String, Object>> res = mapper.selectUsers(params);System.out.println(res);}

在这里插入图片描述

AbstractSQL类源码分析

SQL继承至AbstractSQL类,只重写了该类的getSelf()方法,所有的功能由AbstractSQL类完成,AbstractSQL类中维护了一个SQLStatement内部类的实例和一系列前面提到过的构造SQL语句的方法,例如SELECT()、UPDATE()等方法。

  public enum StatementType {DELETE, INSERT, SELECT, UPDATE}
  StatementType statementType;List<String> sets = new ArrayList<>();List<String> select = new ArrayList<>();List<String> tables = new ArrayList<>();List<String> join = new ArrayList<>();List<String> innerJoin = new ArrayList<>();List<String> outerJoin = new ArrayList<>();List<String> leftOuterJoin = new ArrayList<>();List<String> rightOuterJoin = new ArrayList<>();List<String> where = new ArrayList<>();List<String> having = new ArrayList<>();List<String> groupBy = new ArrayList<>();List<String> orderBy = new ArrayList<>();List<String> lastList = new ArrayList<>();List<String> columns = new ArrayList<>();List<List<String>> valuesList = new ArrayList<>();boolean distinct;String offset;String limit;LimitingRowsStrategy limitingRowsStrategy = LimitingRowsStrategy.NOP;
 switch (statementType) {case DELETE:answer = deleteSQL(builder);break;case INSERT:answer = insertSQL(builder);break;case SELECT:answer = selectSQL(builder);break;case UPDATE:answer = updateSQL(builder);break;default:answer = null;}

SQLStatement内部类用于描述一个SQL语句,该类中通过StatementType确定SQL语句的类型。SQLStatement类中还维护了一系列的ArrayList属性,当调用SELECT()、UPDATE()等方法时,这些方法的参数内容会记录在这些ArrayList对象中。
AbstrastSQL类重写了toString()方法,该方法中会调用SQLStatement对象的sql()方法生成SQL字符串。这里会根据不同的SQL类型进行不同类型的SQL语句拼接。

   private String selectSQL(SafeAppendable builder) {if (distinct) {sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");} else {sqlClause(builder, "SELECT", select, "", "", ", ");}sqlClause(builder, "FROM", tables, "", "", ", ");joins(builder);sqlClause(builder, "WHERE", where, "(", ")", " AND ");sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");sqlClause(builder, "HAVING", having, "(", ")", " AND ");sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");limitingRowsStrategy.appendClause(builder, offset, limit);return builder.toString();}
  @Testpublic void testSelectSQL2() {String newSql = new SQL() {{SELECT("name,mobile_no,age");FROM("t_user A");INNER_JOIN("student B on B.name = A.name");WHERE("B.name like ?");OR();WHERE("A.ID = ?");ORDER_BY("A.ID DESC");}}.toString();System.out.println(newSql);}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/10147.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Vue2进阶

1.el安装 官网地址 Element - The worlds most popular Vue UI frameworkElement&#xff0c;一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库https://element.eleme.cn/#/zh-CN 安装 npm install element-ui -S 引入组件&#xff08;在 main.js&#xf…

案例:三次锁定(下)

第二步: 在 Form1.cs 中完成以下代码 using Dome16_三次锁定.service; using Dome16_三次锁定.service.serviceimpl; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using Sys…

前端学习笔记—Vue3特性

一、 Vue3与Vite构建工具简介 image.png image.png image.png image.png Vite构建工具&#xff08;其他的打包工具有webpack&#xff0c;grunt&#xff0c;gulp&#xff09; image.png image.png 构建 二、创建Vue3项目 vite在TypeScript结合使用上&#xff0c;直接开箱即用&am…

iOS 去掉URL里面的百分号符号

遇到这个一段字符串 “publicId2030095197043302&publicBizTypeCONTENT_USER&chInfoch_life__chsub_Ndiscovery.featured&logoUrlhttps%3A%2F%2Fmdn.alipayobjects.com%2Fopen_content%2Fafts%2Fimg%2FA*_SUKQodfigcAAAAAAAAAAAAAfVx1AQ%2Foriginal&publicName…

node.js安装配置(Windows)

1、下载 CNPM Binaries Mirror 2、安装 3、验证 win R 进入cmd 4、配置环境变量 4.1、创建两个文件夹 4.2、安装目录进入cmd(配置全局属性) 配置两个命令&#xff1a; npm config set prefix "D:\liyunqing\nodejs\node_global"npm config set cache "D:\l…

jdbc中预防SQL攻击

目录 展示SQL攻击 举一个出现sql 攻击的例子 SQL攻击的原因 分析原因 阻止SQL攻击 PreparedStatement的含义 使用PreparedStatement的原因 步骤如下 注意 总结 展示SQL攻击 举一个出现sql 攻击的例子 假设我们在做登录业务时&#xff0c;思路是这样的&#xff1a; 首…

30.1 时序数据库TSDB的典型特点

本节重点介绍 : db-ranking网站对db进行排名时序数据特点时序数据库特点时序数据库遇到的挑战开源时间序列数据库 db-ranking 一个神奇的网站 https://db-engines.com/en/ranking 时序数据ranking https://db-engines.com/en/ranking/timeseriesdbms 排名方法 https://db-en…

14:00面试,14:06就出来了,问的问题过于变态了。。。

我从一家小公司转投到另一家公司&#xff0c;期待着新的工作环境和机会。然而&#xff0c;新公司的加班文化让我有些始料未及。虽然薪资相对较高&#xff0c;但长时间的工作和缺乏休息使我身心俱疲。 就在我逐渐适应这种高强度的工作节奏时&#xff0c;公司突然宣布了一则令人…

架构师之路-学渣到学霸历程-49

实现不同终端的跳转实验 今天分享一下&#xff1a; nginx的跳转&#xff1a;主要看你的访问是手机还是网页&#xff1b;于是就有这个跳转的功能&#xff1b; 1、基础的环境部署 安装好nginx&#xff08;这里最好的就是干净的环境&#xff09;创建两个server&#xff1b;用于…

RK3568笔记1:BootRom

BootRom是瑞芯微公司在生产的CPU时&#xff0c;存储在内部flash中的一段固件代码&#xff0c;用于初始化硬件和启动系统。 RK3568 处理器也具备 BootROM。这是存储在处理器内部的只读存储器 (ROM) 中的一段代码&#xff0c;通常是不可修改的&#xff0c;其主要功能是在设备加电…

DataWind将字符串数组拆出多行的方法

摘要&#xff1a; 可视化建模中先将字符串split为array再用explode(array)即可 可视化建模 进入“可视化建模”页面 1.1 新建任务 如果团队内没有可视化建模任务。请点击“新建任务”&#xff0c;输入名称并确定。 1.2 建立数据连接 在左边栏中选择“数据连接”&#xff0c…

2024年【电工(中级)】考试及电工(中级)考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 电工&#xff08;中级&#xff09;考试参考答案及电工&#xff08;中级&#xff09;考试试题解析是安全生产模拟考试一点通题库老师及电工&#xff08;中级&#xff09;操作证已考过的学员汇总&#xff0c;相对有效帮…

【韩老师零基础30天学会Java】02章

sublime Text中本身没有GBK编码&#xff0c;需要安装 要在sublime Text中设置编码为GBK&#xff0c;请按照以下步骤操作 打开Sublime Text编辑器,点击菜单栏中的“Preferences”(首选项)选项&#xff0c;找打Package Control选项。点击Package Control&#xff0c;随后搜索Inst…

Pytorch实现运动鞋识别

Pytorch实现运动鞋识别 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 电脑系统&#xff1a;Windows11 显卡型号&#xff1a;NVIDIA Quadro P620 语言环境&#xff1a;python 3.9.7 编译器&#xff1a;j…

老年人跌倒智能检测系统

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…

Spring Boot原理全解析:如何让开发更轻松高效(二)-起步依赖、自动装配

通过这篇博客&#xff0c;读者将能够掌握 Spring Boot 中的配置优先级和 Bean 管理的核心原理&#xff0c;为开发更加高效、可维护的 Spring Boot 应用打下坚实的基础。 目录 前言 起步依赖 自动配置 概述 常见方案 概述 方案一 方案二 总结 前言 通过这篇博客&#xf…

测试实项中的偶必现难测bug--短信触发H5拒绝行为

问题描述: 企业邀请其他人加入团队,发送邀请短信给对方,对方通过短信链接跳转到H5页面,输入手机后,点击发送验证码,前提是短信通知验证弹窗需要打开,收到短信验证码后,点击一键代入,会触发拒绝加入行为。 需求: 由于我们的邀请链接是一次性的,一旦有用户确认加入或…

PVE纵览-PVE与VM:谁才是你的最佳虚拟化选择?

PVE纵览-PVE与VM&#xff1a;谁才是你的最佳虚拟化选择&#xff1f; 文章目录 PVE纵览-PVE与VM&#xff1a;谁才是你的最佳虚拟化选择&#xff1f;摘要1 不同虚拟化平台的基础2 平台特性与功能3 性能与可靠性4 成本与经济性5 应用场景比较6 用户体验与支持7 结论与建议 关键字&…

陪诊问诊APP开发实战:基于互联网医院系统源码的搭建详解

时下&#xff0c;开发一款功能全面、用户体验良好的陪诊问诊APP成为了医疗行业的一大热点。本文将结合互联网医院系统源码&#xff0c;详细解析陪诊问诊APP的开发过程&#xff0c;为开发者提供实用的开发方案与技术指导。 一、陪诊问诊APP的背景与功能需求 陪诊问诊APP核心目…

post sim下如何将timing信息反标到仿真工具

文章目录 0 前言1 调用格式2 option介绍2.1 sdf_file (**必须**)2.2 module_instance (**可选**)2.3 config_file (**可选**)2.4 log_file (**可选**)2.5 mtm_spec (**可选**)2.6 scale_factors (**可选**)2.7 scale_type (**可选**) 0 前言 跑post sim时需要带入timing信息&a…