学习笔记- MyBatis 动态 SQL

目录

1. 什么是动态SQL

2. 数据库

3. IF

1. Mapper 层 定义方法

2. XML配置文件编写 SQL 语句

4. where

5. choose

6. foreach

1. 批量添加

2. 批量删除


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 中文网

mybatis – MyBatis 3 | 动态 SQL

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>

测试结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值