MyBatis是一款强大的持久层框架,提供了丰富的功能来简化数据库操作。其中之一就是动态SQL,它可以根据不同的条件生成不同的SQL语句,让我们能够更灵活地构建和执行数据库操作。
动态SQL在实际开发中非常有用,特别是当需要根据不同的查询条件来拼接SQL语句时。MyBatis提供了多种方式来实现动态SQL,包括使用if、choose、when、otherwise、foreach等标签。下面我们将逐一介绍这些标签的用法和示例。
下面是此文章用到的表
供货商表:
/*
Navicat Premium Data Transfer
Source Server : MySql
Source Server Type : MySQL
Source Server Version : 80013 (8.0.13)
Source Host : localhost:3306
Source Schema : yyds
Target Server Type : MySQL
Target Server Version : 80013 (8.0.13)
File Encoding : 65001
Date: 11/08/2023 16:01:17
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_supplier
-- ----------------------------
DROP TABLE IF EXISTS `t_supplier`;
CREATE TABLE `t_supplier` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '供货商ID(主键)',
`supCode` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '供货商编码',
`supName` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '供货商名称',
`supDesc` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '供货商描述',
`supContact` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '联系人',
`supPhone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '联系电话',
`supAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '地址',
`supFax` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '传真',
`createdUserId` bigint(20) NOT NULL COMMENT '创建人ID',
`createdTime` date NOT NULL COMMENT '创建时间',
`updateUserId` bigint(20) NOT NULL COMMENT '修改人ID',
`updatedTime` date NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
入库记录表:
/*
Navicat Premium Data Transfer
Source Server : MySql
Source Server Type : MySQL
Source Server Version : 80013 (8.0.13)
Source Host : localhost:3306
Source Schema : yyds
Target Server Type : MySQL
Target Server Version : 80013 (8.0.13)
File Encoding : 65001
Date: 11/08/2023 16:01:28
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_storagerecord
-- ----------------------------
DROP TABLE IF EXISTS `t_storagerecord`;
CREATE TABLE `t_storagerecord` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '入库记录ID(主键)',
`srCode` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入库记录编码',
`goodsName` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品名称',
`goodsDesc` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品描述',
`goodsUnit` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品单位',
`goodsCount` decimal(20, 2) NOT NULL COMMENT '入库数量',
`totalAmount` decimal(20, 2) NOT NULL COMMENT '商品总额',
`payStatus` int(11) NOT NULL COMMENT '支付状态(未支付——1 ,已支付——2)',
`supplierId` bigint(20) NOT NULL COMMENT '供货商ID(取自供货商表) - 供货商ID',
`createdUserId` bigint(20) NOT NULL COMMENT '创建人ID',
`createdTime` date NOT NULL COMMENT '创建时间',
`updatedUserId` bigint(20) NOT NULL COMMENT '修改人ID',
`updatedTime` date NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
偷点小懒😊我就直接将要用到的字段分别封装成不同的实体类了
1、if标签
相当于Java中的if语句
语法为:
<if test = "条件判断,返回true或false" >
SQL语句
</if>
需求:根据供货商ID、支付状态、商品名称(模糊查询)为条件查询入库记录表中的入库记录ID、入库记录编号、商品名称、供货商ID、供货商名称、商品总额、支付状态和创建时间
接口中的方法:
List<First> selectBySupplierIdAndPayStatusAndGoodsName(@Param("supplierId") Integer supplierId, @Param("payStatus") Integer payStatus, @Param("goodsName") String goodsName);
mapper映射文件:
<select id="selectBySupplierIdAndPayStatusAndGoodsName" resultType="pojo.First">
select sg.id,
sg.srCode,
sg.goodsName,
sg.supplierId,
sl.supName,
sg.totalAmount,
sg.payStatus,
sg.createdTime
from t_storagerecord sg
left join t_supplier sl on sg.supplierId = sl.id
where 1=1
<if test="supplierId != null">
and supplierId = #{supplierId}
</if>
<if test="payStatus != null">
and payStatus = #{payStatus}
</if>
<if test="goodsName != null and goodsName != ''">
and goodsName like "%"#{goodsName}"%"
</if>
</select>
测试:
@Test
public void First() {
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
List<First> Firsts= mapper.selectBySupplierIdAndPayStatusAndGoodsName(null, null, "k");
sz1s.forEach(first-> System.out.println(first));
}
测试的结果为
因为我测试类中前两个参数为null所有它不会将这两个条件加进去,所有执行的sql语句为
select sg.id, sg.srCode, sg.goodsName, sg.supplierId, sl.supName, sg.totalAmount, sg.payStatus, sg.createdTime from t_storagerecord sg left join t_supplier sl on sg.supplierId = sl.id where 1=1 and goodsName like "%"?"%"
将数据库中商品名称中包含k的记录查询出来
2、where标签
在MyBatis中,<where>标签用于在SQL语句中动态生成WHERE子句。它可以根据条件判断动态添加查询条件,并且会自动处理条件之间的逻辑关系。
使用where标签之后可以将上面语句中where条件省略
语法:
<where>
<if test="条件判断">
SQL语句
</if>
…
</where>
需求:以供货商编码(模糊查询)、供货商名称(模糊查询)为条件查询供货商表中的供货商ID、供货商编码、供货商名称、联系人、传真和创建时间字段 用if\where标签实体类:
接口中的方法:
List<Second> selectBySupCodeAndSupName(@Param("supCode") String supCode, @Param("supName") String supName);
mapper映射文件:
<select id="selectBySupCodeAndSupName" resultType="pojo.Second">
select id, supCode, supName, supContact, supPhone, supFax, createdTime
from t_supplier
<where>
<if test="supCode != null and supCode != ''">
and supCode like "%"#{supCode}"%"
</if>
<if test="supName != null and supName != ''">
and supName like "%"#{supName}"%"
</if>
</where>
</select>
测试:
@Test
public void Second() {
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
List<Second> seconds= mapper.selectBySupCodeAndSupName("1", "");
seconds.forEach(second-> System.out.println(second));
}
因为我在测试类中第二个参数给他的值为空 ,所有
执行的sql语句为:
select id, supCode, supName, supContact, supPhone, supFax, createdTime from t_supplier WHERE supCode like "%"?"%"
将数据库中供货商编码 中包含1的记录查出来
3、choose(when、otherwise)标签
是一个组合标签,通常与when、otherwise标签配合使用 类似于Java中switch语句
choose相当于switch、when相当于case\otherwise相当于default
语法为:
<choose>
<when test="条件判断,返回true或false">
</when>
<when test="条件判断,返回true或false">
</when>
...
<otherwise>
</otherwise>
</choose>
需求:以供货商编码(模糊查询)、供货商名称(模糊查询)、联系人(模糊查询)创建时间为条件查询供货商id、供货商编码、供货商名称、联系人、联系电话、传真、创建时间。
供货商名称、供货商编码、联系人姓名中任意一个参数不为空时,以该非空的参数作为匹配条件进行查询,其余参数忽略 如果三个参数都为空,则使用创建时间作为匹配条件进行查询
实体类还是上面那个Second
接口中的方法:
List<Second> selectBySupNameOrSupCodeOrSupContactOrCreatedTime(@Param("supName") String supName, @Param("supCode") String supCode, @Param("supContact") String supContact, @Param("createdTime") LocalDate createdTime);
映射文件:
<select id="selectBySupNameOrSupCodeOrSupContactOrCreatedTime" resultType="pojo.Second">
select id, supCode, supName, supContact, supPhone, supFax, createdTime
from t_supplier
<where>
<choose>
<when test="supName != null and supName != ''">
and supName like "%"#{supName}"%"
</when>
<when test="supCode != null and supCode != ''">
and supCode like "%"#{supCode}"%"
</when>
<when test="supContact != null and supContact != ''">
and supContact like "%"#{supContact}"%"
</when>
<otherwise>
and createdTime = #{createdTime}
</otherwise>
</choose>
</where>
</select>
测试类:
@Test
public void ChooseFirst() {
String dateString = "2006-01-27";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate localDate = LocalDate.parse(dateString, formatter);
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
List<Second> seconds= mapper.selectBySupNameOrSupCodeOrSupContactOrCreatedTime("", "1", "", localDate);
sz23s.forEach(second-> System.out.println(second));
}
测试sql为:
select id, supCode, supName, supContact, supPhone, supFax, createdTime from t_supplier WHERE supCode like "%"?"%"
查询数据库中供货商编码中包含1的记录
4、foreach标签
循环数组或集合,动态生成sql,通常用于in条件,通常用于批量添加和删除和查询,比如这样的SQL:
//批量删除
delete from 表名 where id in(1,2,3);
//批量新增
insert into 表名(表中的字段)values
(值1),
(值2),
(值3)
//条件查询
select * from 表名 where 条件 in("","","")
语法为:
<foreach collection = "参数名称"
item = "元素别名"
open = "("
separator = ","
close = ")"
index = "当前元素位置下标" >
#{元素别名}
</foreach>
参数:
-
collection:指定要遍历的集合或数组的参数名。可以是一个参数,也可以是一个Ognl表达式,用来指定要遍历的集合或数组。
-
item:指定在遍历过程中每个元素的别名。可以在<foreach>标签内使用这个别名来引用每个元素。
-
index:指定在遍历过程中每个元素的索引位置。可以在<foreach>标签内使用这个索引来引用每个元素的位置。
-
open:指定遍历过程中在结果字符串的开头添加的字符串。可以用来添加一些前缀。
-
close:指定遍历过程中在结果字符串的结尾添加的字符串。可以用来添加一些后缀。
-
separator:指定遍历过程中每个元素之间的分隔符。可以用来指定元素之间的连接符。
需求:
第一个练习同时查询多个供货商关联的入库单列表数据 要求分别以数组、List集合为参数
第二个练习跟上面的要求差不多,参数为Map集合,和入库编码(模糊查询)
接口中的方法:
//数组方式
List<Thrid> selectAllByArray(@Param("supplierIds") Integer[] supplierIds);
//集合方式
List<Fourth> selectAllByList(@Param("supplierIds") List<Integer> supplierIds);
//Map
List<Fifth> selectAllByMap(@Param("supplierIds") List<Integer> supplierIds, @Param("srCode") Map<String, String> srCode);
映射文件:
<select id="selectAllByArray" resultType="pojo.Thrid">
select sg.id,
sg.srCode,
sg.goodsName,
sg.goodsDesc,
sg.goodsUnit,
sg.goodsCount,
sg.totalAmount,
sg.payStatus,
sg.supplierId,
sg.createdUserId,
sg.createdTime,
sg.updatedUserId,
sg.updatedTime
from t_storagerecord sg,
t_supplier sl
where sg.supplierId = sl.id
and sg.supplierId in
<foreach collection="supplierIds" item="supplierId" open="(" separator="," close=")">
#{supplierId}
</foreach>
</select>
<select id="selectAllByList" resultType="pojo.Fourth">
select sg.id,
sg.srCode,
sg.goodsName,
sg.goodsDesc,
sg.goodsUnit,
sg.goodsCount,
sg.totalAmount,
sg.payStatus,
sg.supplierId,
sg.createdUserId,
sg.createdTime,
sg.updatedUserId,
sg.updatedTime
from t_storagerecord sg,
t_supplier sl
where sg.supplierId = sl.id
and sg.supplierId in
<foreach collection="supplierIds" item="supplierId" open="(" separator="," close=")">
#{supplierId}
</foreach>
</select>
<select id="selectAllByMap" resultType="pojo.Fifth">
select sg.id,
sg.srCode,
sg.goodsName,
sg.goodsDesc,
sg.goodsUnit,
sg.goodsCount,
sg.totalAmount,
sg.payStatus,
sg.supplierId,
sg.createdUserId,
sg.createdTime,
sg.updatedUserId,
sg.updatedTime
from t_storagerecord sg,
t_supplier sl
where sg.supplierId = sl.id
and sg.supplierId in
<foreach collection="supplierIds" item="supplierId" open="(" separator="," close=")">
#{supplierId}
</foreach>
<!--判断了 srCode 参数是否不为 null 且 srCode Map 的大小大于 0。如果满足条件,将会拼接一个模糊查询的条件语句。-->
<if test="srCode != null and srCode.size() > 0">
and srCode like "%"#{srCode.srCode}"%" <!-- srCode.get("srCode")-->
</if>
</select>
测试类:
@Test
public void Array() {
Integer[] id = {1, 2};
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
List<Third> arrays= mapper.selectAllByArray(id);
arrays.forEach(array-> System.out.println(array));
}
@Test
public void List() {
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
List<Fourth> lists= mapper.selectAllByList(integerList);
sz45s.forEach(list-> System.out.println(list));
}
@Test
public void Map() {
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
Map<String,String> map = new HashMap<>();
map.put("srCode","1");
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
List<Fifth> fifths= mapper.selectAllByMap(integerList, map);
sz45s.forEach(fifth-> System.out.println(fifth));
}
测试的sql分别为:
//数组和集合执行的
select sg.id, sg.srCode, sg.goodsName, sg.goodsDesc, sg.goodsUnit, sg.goodsCount, sg.totalAmount, sg.payStatus, sg.supplierId, sg.createdUserId, sg.createdTime, sg.updatedUserId, sg.updatedTime from t_storagerecord sg, t_supplier sl where sg.supplierId = sl.id and sg.supplierId in ( ? , ? )
//Map执行的
select sg.id, sg.srCode, sg.goodsName, sg.goodsDesc, sg.goodsUnit, sg.goodsCount, sg.totalAmount, sg.payStatus, sg.supplierId, sg.createdUserId, sg.createdTime, sg.updatedUserId, sg.updatedTime from t_storagerecord sg, t_supplier sl where sg.supplierId = sl.id and sg.supplierId in ( ? , ? ) and srCode like "%"?"%"
数组和集合查询供货商ID为(1,2)的数据
Map查询供货商ID为(1,2)并且入库编码中含1的数据
5、set标签
简化SQL语句中set子句处理 智能忽略更新语句尾部多出来的逗号
语法:
<set>
<if test="条件判断">
SQL语句
</if>
…
</set>
需求:更新供货商表中id为1的供货商名称 供货商描述 联系电话 其余字段的值保持不变
接口中的方法:
Integer update(Sixth sixth);
映射文件:
<update id="update" parameterType="pojo.Sixth">
update t_supplier
<set>
<if test="supName != null">supName = #{supName},</if>
<if test="supDesc != null">supDesc = #{supDesc},</if>
<if test="supPhone != null">supPhone = #{supPhone},</if>
</set>
where id = #{id}
</update>
测试类:
@Test
public void Set() {
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
Sixth sixth= new Sixth(2,"杨俊豪","不晓得是什么东西","13838389438");
Integer update = mapper.update(sz6);
System.out.println(update);
sqlSession.commit(); //增删改一定要记得调用commit(提交)!!!
}
因为我测试类中参数是全部填写了的所有就将三个值全部修改了
6、trim标签
动态地为SQL语句添加前后缀 智能忽略标签前后多余的and、or或逗号等字符
语法:
<trim prefix = "前缀"
suffix = "后缀"
prefixOverrides = "忽略前缀"
suffixOverrides = "忽略后缀" >
…
</trim>
- prefix:指定在SQL语句片段的开头添加的字符串。
- prefixOverrides:指定需要移除的SQL语句片段的前缀字符串。
- suffix:指定在SQL语句片段的结尾添加的字符串。
- suffixOverrides:指定需要移除的SQL语句片段的后缀字符串。
需求:跟上面set标签中的需求一样,只不过是用trim修改下
Integer updateByTrim(Sixth sixth);
映射文件:
<update id="updateByTrim" parameterType="Sixth">
update t_supplier
<trim prefix="SET" suffixOverrides="," suffix="where id = #{id}">
<if test="supName != null">
supName = #{supName},
</if>
<if test="supDesc != null">
supDesc = #{supDesc},
</if>
<if test="supPhone != null">
supPhone = #{supPhone},
</if>
</trim>
</update>
测试类:
@Test
public void Trim() {
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
Sixth sixth = new sixth(2,"杨俊豪","母鸡啊","13838389438");
Integer a = mapper.updateByTrim(sixth);
System.out.println(a);
sqlSession.commit();
}
结果跟上面set标签一样
7.Mybatis分页功能实现
关键字:limit
pageBegin:页码起始位置
pageSize:每页展示的数据量
需求:分页查询供货商表和入库记录表,条件是根据供货商名称模糊查询、查询结果以创建时间降序排列
接口中的方法:
List<Seventh> selectLimit(@Param("supName") String supName, @Param("pageBegin") Integer pageBegin, @Param("pageSize") Integer pageSize);
映射文件:
<select id="selectLimit" resultType="pojo.Seventh">
select sl.id,
sl.supCode,
sl.supName,
sl.supDesc,
sl.supContact,
sl.supPhone,
sl.supAddress,
sl.supFax,
sl.createdUserId,
sl.createdTime,
sl.updateUserId,
sl.updatedTime,
sg.srCode,
sg.goodsName,
sg.goodsDesc,
sg.goodsUnit,
sg.goodsCount,
sg.totalAmount,
sg.payStatus
from t_supplier sl
left join t_storagerecord sg on sl.id = sg.supplierId
<trim prefix="where" prefixOverrides="and | or">
<if test="supName != null and supName != ''">
supName like "%"#{supName}"%"
</if>
</trim>
order by sl.createdTime desc
limit #{pageBegin},#{pageSize}
</select>
测试类:
@Test
public void Limit() {
SqlSession sqlSession = SqlSessionUtil.openSession();
SzMapper mapper = sqlSession.getMapper(SzMapper.class);
Integer pageIndex = 1;
Integer pageSize = 2;
Integer pageBegin = (pageIndex -1) * pageSize;
List<Seventh> sevenths= mapper.selectLimit("杨", pageBegin, pageSize);
sz8s.forEach(seventh-> System.out.println(seventh));
}
执行的sql为
select sl.id, sl.supCode, sl.supName, sl.supDesc, sl.supContact, sl.supPhone, sl.supAddress, sl.supFax, sl.createdUserId, sl.createdTime, sl.updateUserId, sl.updatedTime, sg.srCode, sg.goodsName, sg.goodsDesc, sg.goodsUnit, sg.goodsCount, sg.totalAmount, sg.payStatus from t_supplier sl left join t_storagerecord sg on sl.id = sg.supplierId where supName like "%"?"%" order by sl.createdTime desc limit ?,?
8、sql标签与include标签
sql标签用来声明sql片段
include标签用来将声明的sql片段包含到某个sql语句当中
作用:代码复用。易维护。
语法:
<sql id="sql_fragment_id">
-- SQL语句片段
</sql>
<include refid="sql_fragment_id" />
下面是一个例子,与上面的表无关
<sql id="carCols">
<!--定义要查的字段-->
id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType
</sql>
<select id="selectAllRetMap" resultType="map">
select <include refid="carCols"/> from t_car
</select>
<select id="selectAllRetListMap" resultType="map">
select <include refid="carCols"/> carType from t_car
</select>
<select id="selectByIdRetMap" resultType="map">
select <include refid="carCols"/> from t_car where id = #{id}
</select>
通过以上示例,我们可以看到MyBatis动态SQL的强大之处。我们可以根据不同的条件灵活地构建SQL语句,而无需手动拼接字符串,大大提高了开发效率和代码的可读性。
总结起来,MyBatis中的动态SQL是一项非常强大和实用的功能,能够帮助我们更轻松地构建和执行数据库操作。通过使用if、choose、when、otherwise、foreach等标签,我们可以根据不同的条件生成不同的SQL语句,让数据库操作更加灵活和高效。
ok,今天的分享就到此结束了,希望以上内容对你有所帮助
关注博主不迷路,期待你的关注!!!