目录
1. 什么是动态SQL
MyBatis 的动态 SQL 是一种强大的特性,它允许根据不同的条件动态地生成 SQL 语句,从而提高了 SQL 语句的灵活性和可复用性。
2. 数据库
CREATE TABLE `cy_card` (
`card_id` int(11) NOT NULL AUTO_INCREMENT,
`card_prefix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '卡前缀',
`card_number` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '卡号',
`card_money` decimal(10,2) DEFAULT '0.00' COMMENT '卡余额',
`card_is_delete` tinyint(1) DEFAULT '0' COMMENT '卡片是否被删除1是0否',
`card_status` tinyint(1) DEFAULT '0' COMMENT '卡状态0待领用1待使用2已使用',
`card_create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '卡生成时间',
`card_update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '卡信息更新时间',
PRIMARY KEY (`card_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of cy_card
-- ----------------------------
INSERT INTO `cy_card` VALUES ('1', 'cy', 'cy00000001', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('2', 'cy', 'cy00000002', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('3', 'cy', 'cy00000003', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('4', 'cy', 'cy00000004', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('5', 'cy', 'cy00000005', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('6', 'cy', 'cy00000006', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('7', 'cy', 'cy00000007', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('8', 'cy', 'cy00000008', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('9', 'cy', 'cy00000009', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('10', 'cy', 'cy00000010', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('11', 'cy', 'cy00000011', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('12', 'cy', 'cy00000012', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('13', 'cy', 'cy00000013', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('14', 'cy', 'cy00000014', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('15', 'cy', 'cy00000015', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('16', 'cy', 'cy00000016', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('17', 'cy', 'cy00000017', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('18', 'cy', 'cy00000018', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('19', 'cy', 'cy00000019', '0.00', '0', '0', null, null);
INSERT INTO `cy_card` VALUES ('20', 'cy', 'cy00000020', '0.00', '0', '0', null, null);
POJO 实体类
public class CyCard {
private long cardId;
private String cardPrefix;
private String cardNumber;
private double cardMoney;
private long cardIsDelete;
private long cardStatus;
private java.sql.Timestamp cardCreateTime;
private java.sql.Timestamp cardUpdateTime;
// ...set/get 方法
}
mybatis 中文网
3. IF
例子:我们在淘宝或者京东搜索商品的时候,有个类型选择,比如 可以选择宝贝,天猫或者店铺 来进行搜索
多个条件查询就可以用到 动态SQL 的 IF 标签
我们这里使用一卡通的卡进行多条件查询
1. Mapper 层 定义方法
2. XML配置文件编写 SQL 语句
test属性指定了判断的条件,如果条件成立,则会执行<if>标签内部的SQL语句。
<select id="GetCardsInfo" resultType="com.POJO.CyCard">
SELECT * FROM cy_card
WHERE
<if test="cardId != null and cardId != ''" >
card_id = #{cardId}
</if>
<if test="cardNumber != null and cardNumber != ''">
AND card_number = #{cardNumber}
</if>
</select>
这个SQL语句提供了根据 Id 或卡号搜索 卡的信息,细心的读者可能会发现这条SQL存在的问题
1. 如果没有匹配的条件会怎么样?那么SQL 语句就会是这样的
SELECT * FROM cy_card
WHERE
2. 如果匹配的只有第二个条件会怎么样?那么 SQL 语句就会变成这样
SELECT * FROM cy_card
WHERE
AND card_number = someNumber
这都会导致查询失败,那如何解决呢?
两种办法,添加 WHERE 1=1 或者 where 标签
WHERE 1 = 1
<select id="GetCardsInfo" resultType="com.POJO.CyCard">
SELECT * FROM cy_card
WHERE 1 = 1
<if test="cardId != null and cardId != ''" >
AND card_id = #{cardId}
</if>
<if test="cardNumber != null and cardNumber != ''">
AND card_number = #{cardNumber}
</if>
</select>
4. where
where 元素只会在子元素有任意一条 IF 语句成立的情况下才插入 “WHERE” 子句。
而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
注意:除了第一个 IF 语句的条件不需要加 AND 关键字,其他都要加
<select id="GetCardsInfo" resultType="com.POJO.CyCard">
SELECT * FROM cy_card
<where>
<if test="cardId != null and cardId != ''" >
card_id = #{cardId}
</if>
<if test="cardNumber != null and cardNumber != ''">
AND card_number = #{cardNumber}
</if>
</where>
</select>
5. choose
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
when:用于定义条件成立时执行的代码块。
otherwise:用于定义默认的代码块,当所有的 <when> 条件都不成立时,将行 <otherwise> 中定义的代码块
<select id="GetCardsInfo" resultType="com.POJO.CyCard">
SELECT * FROM cy_card
<where>
<choose>
<when test="cardId != null and cardId != ''"> <!-- 相当于case -->
card_id = #{cardId}
</when>
<when test="cardNumber != null and cardNumber != ''">
AND card_number = #{cardNumber}
</when>
<!-- 当cardId和cardNumber参数都为空时,查询所有 card_number 记录 -->
<otherwise>
AND card_number LIKE '%'
</otherwise>
</choose>
</where>
</select>
传入两个参数
当传入两个参数时,choose 标签会选择第一个条件成立的 when 标签
传参一个 cardNumber 参数的情况
两个参数都不匹配的情况
6. foreach
可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。
当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
collection 属性:指定要遍历的集合
item 属性:本次迭代获取到的元素。
separator 属性:元素之间的分隔符,
open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
1. 批量添加
注意:在进行增删改的数据库操作时,涉及到数据的变动,需要开启提交事务,MyBatis 默认开启事务,但没有开启提交事务
1. 定义 Mapper 接口方法
public interface CardMapper {
ArrayList<CyCard> GetCardsInfo(@Param("cardId")int cardId, @Param("cardNumber") String CardNumber);
int AddCards(@Param("cardPrefix") String cardPrefix, @Param("numbers") ArrayList<String> numbers);
Boolean CheckCardNumber(String cardNumber);
}
2. 写 SQL 语句
<insert id="AddCards">
INSERT INTO cy_card (card_prefix,card_number)
VALUES
<foreach collection="numbers" item="item" separator=",">
(#{cardPrefix},#{item})
</foreach>
</insert>
<select id="CheckCardNumber" resultType="java.lang.Boolean">
SELECT EXISTS(SELECT card_number FROM cy_card WHERE card_number = #{cardNumber})
</select>
3. Service 层
@Override
public responseDTO AddCards(String cardPrefix, int CardNumberStart, int CardNumberEnd) {
ArrayList<String> numbers = new ArrayList<>();
// 存在重复的记录
ArrayList<String> existsNumbers = new ArrayList<>();
int result = 0;
for (int i = CardNumberStart; i <= CardNumberEnd; i++) {
String number = cardPrefix + String.format("%07d", i); // 拼接卡号,不足7位,前面补0
numbers.add(number);
}
// 检查是否有存在的记录
for (int i = 0; i < numbers.size(); i++) {
if (cardMapper.CheckCardNumber(numbers.get(i))) {
existsNumbers.add(numbers.get(i));
}
}
// 移除重复的记录
numbers.removeAll(existsNumbers);
if (!numbers.isEmpty()) {
result = cardMapper.AddCards(cardPrefix, numbers);
}
if (result == numbers.size() && result != 0) {
return new responseDTO(200, numbers, "添加成功,重复记录:" + existsNumbers, result);
} else {
return new responseDTO(201, existsNumbers, "添加失败,成功记录:" + numbers, result);
}
}
4. Controller 控制层
5. 运行结果,生成的动态 SQL 语句
2. 批量删除
定义 Mapper 层接口的方法
写 SQL 语句,动态生成删除记录的条件
<delete id="DeleteCards">
DELETE FROM cy_card WHERE card_number
IN
<foreach collection="numbers" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</delete>
测试结果