JB3-1-MyBatis(一)

Java道经第3卷 - 第1阶 - MyBatis(一)


传送门:JB3-1-MyBatis(一)
传送门:JB3-1-MyBatis(二)

心法:本章使用 Maven 父子结构项目进行练习

练习项目结构如下

|_  v3-1-ssm-mybatis
	|_ test

武技:搭建练习项目结构

  1. 创建父项目 v3-1-ssm-mybatis,删除 src 目录。
  2. 在父项目中添加依赖:
<properties>
	<maven.compiler.source>17</maven.compiler.source>
	<maven.compiler.target>17</maven.compiler.target>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<junit.version>4.13.2</junit.version>
	<lombok.version>1.18.24</lombok.version>
	<hutool.version>5.8.25</hutool.version>
	<mysql-connector-j.version>8.2.0</mysql-connector-j.version>
	<mybatis.version>3.5.13</mybatis.version>
</properties>

<dependencies>
	<!--junit-->
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>${junit.version}</version>
		<scope>test</scope>
	</dependency>
	<!--lombok-->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<version>${lombok.version}</version>
		<scope>provided</scope>
	</dependency>
	<!--hutool-all-->
	<dependency>
		<groupId>cn.hutool</groupId>
		<artifactId>hutool-all</artifactId>
		<version>${hutool.version}</version>
	</dependency>
</dependencies>
  1. 在子项目 test 中添加依赖:
<!--mysql-connector-j-->
<dependency>
	<groupId>com.mysql</groupId>
	<artifactId>mysql-connector-j</artifactId>
	<version>${mysql-connector-j.version}</version>
	<scope>runtime</scope>
</dependency>
<!--mybatis-->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>${mybatis.version}</version>
</dependency>

S01. MyBatis基础入门

心法:MyBatis 是一个底层半封装了 JDBC 的持久层的开源 ORM 框架,,其前身是 Apache 的 iBatis 项目,2010 年迁移到了 GoogleCode 并改名为 MyBatis,2013 年 11 月迁移到 Github。

半封装框架:MyBatis 封装了驱动,连接,Statement 等业务代码,但不封装 SQL 语句。

MyBatis 核心流程如下图

在这里插入图片描述

E01. MyBatis基础入门

1. MyBatis基础配通

武技:在子项目中整合 MyBatis 框架

  1. 准备 MyBatis 测试数据:
-- db
create database mybatis character set utf8mb4;
use mybatis;

-- table
create table clazz
(
    id         bigint auto_increment comment '主键',
    clazz_name varchar(128) not null default '' comment '班级名称',
    created    datetime     not null default current_timestamp comment '创建时间',
    updated    datetime     not null default current_timestamp comment '修改时间',
    primary key (id)
) comment '班级表';

create table student
(
    id          bigint auto_increment comment '主键',
    name        varchar(128) not null default '' comment '姓名',
    gender      tinyint      not null default 0  comment '性别,0女1男2保密',
    phone       char(11)     not null default '' comment '手机号',
    fk_clazz_id bigint comment '班级表外键',
    created     datetime     not null default current_timestamp comment '创建时间',
    updated     datetime     not null default current_timestamp comment '修改时间',
    primary key (id)
) comment '学生表';

create table department
(
    id         bigint auto_increment comment '主键',
    dname      varchar(128) not null default '' comment '部门名',
    created    datetime     not null default current_timestamp comment '创建时间',
    updated    datetime     not null default current_timestamp comment '修改时间',
    primary key (id)
) comment '部门表';

create table employee
(
    id               bigint auto_increment comment '主键',
    ename            varchar(128) not null default '' comment '员工名',
    phone            char(11)     not null default '' comment '手机号',
    fk_department_id bigint comment '部门表外键',
    created          datetime     not null default current_timestamp comment '创建时间',
    updated          datetime     not null default current_timestamp comment '修改时间',
    primary key (id)
) comment '员工表';

-- data
insert into clazz (id, clazz_name)
values (1, 'J2001'),
       (2, 'J2002');

insert into student (id, name, gender, phone, fk_clazz_id)
values (1, '赵四', 1, '17700000001', 1),
       (2, '刘能', 1, '17700000002', 2);

insert into department (id, dname)
values (1, '设计部'),
       (2, '开发部');

insert into employee (id, ename, phone, fk_department_id)
values (1, '赵四', '17700000001', 1),
       (2, '刘能', '17700000002', 1),
       (3, '广坤', '17700000003', 1),
       (4, '刘英', '17700000004', 2);
  1. 开发 classpath:db.properties 数据源属性文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.40.77:3306/mybatis?serverTimezone=Asia/Shanghai\
  &useUnicode=true&characterEncoding=utf8\
  &useSSL=false&allowPublicKeyRetrieval=true
jdbc.username=joezhou
jdbc.password=joezhou
  1. 开发 classpath:mybatis.xml MyBatis 主配文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  
    <!--从classpath下引入属性文件,不支持 `classpath:` 前缀,后期由Spring代劳-->
    <properties resource="db.properties"/>

    <!--全局配置-->
    <settings>
        <!--执行器类型-->
        <setting name="defaultExecutorType" value="SIMPLE"/>
    </settings>

    <!--环境列表标签: 支持配置多环境-->
    <environments default="test">
        <!--环境标签: 使用JDBC事务(支持提交回滚)-->
        <environment id="test">
            <!--配置该环境使用JDBC事务管理类,此事务支持提交和回滚-->
            <transactionManager type="JDBC"/>
            <!--配置该环境使用POOLED连接池,需注入数据源四项-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
  1. 开发测试类:
package mybatis;

/** @author 周航宇 */
public class MyBatisTest {

    /** 测试MyBatis连接是否成功 */
    @SneakyThrows
    @Test
    public void connection() {

        // 主配文件的位置: 在创建会话工厂的时候,需要读取主配
        String filePath = "mybatis.xml";

        // 将主配文件转成输入字节流,创建会话工厂的时候用
        InputStream resource = Resources.getResourceAsStream(filePath);

        // 通过SqlSessionFactoryBuilder创建SqlSessionFactory,创建时必须读取主配文件
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder()
                .build(resource);

        // 通过SqlSessionFactory开启一个具有事务保护的session会话,用于和数据库打交道
        // 若使用 `sessionFactory.openSession(true)` 则表示开启一个没有事务保护的session会话
        SqlSession sqlSession = sessionFactory.openSession();

        // 从SqlSession会话中获取一个连接
        Connection connection = sqlSession.getConnection();

        // 判断连接是否成功
        System.out.println(connection.isClosed() ? "连接失败" : "连接成功");

        // 关闭会话,清空一级缓存,可使用 `try-with-resource` 结构
        sqlSession.close();
    }
}

2. MyBatis工具封装

心法:封装 MyBatis 工具时需要考虑到 SqlSessionFactoryBuilder, SqlSessionFactory, SqlSession 三个核心对象。

核心对象描述
SqlSessionFactoryBuilder用于创建 session 工厂,用完即删,建议设置为局部变量
SqlSessionFactory可以重复使用,但不该被重复创建,建议以单例模式来管理
SqlSession每个线程都该独享一个 session 会话,不建议被封装

武技:封装 MyBatisUtil 工具类

1.开发 MyBatisUtil 工具类:

package com.joezhou.util;

/** @author 周航宇 */
public class MyBatisUtil {

    /** 开发工厂属性: 饱汉单例不赋初值 */
    private static volatile SqlSessionFactory factory;

    /**
     * 获取单例的session工厂实例
     * @param resource 主配文件的classpath位置,字符串
     * @return 单例的session工厂实例
     */
    @SneakyThrows
    public static SqlSessionFactory getFactory(String resource) {
        // 双重检查锁: DCL第1个if检查用于避免后续线程的加锁接锁开销
        if (factory == null) {
            // 双重检查锁: DCL锁用于避免线程争抢,毁坏单例效果
            synchronized (MyBatisUtil.class) {
                // 双重检查锁: DCL第2个if检查用于使用饱汉单例模式构建工厂实例
                if (factory == null) {
                    // 返回主配的字节流对象,创建Session工厂需要
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    // 创建Session工厂
                    factory = new SqlSessionFactoryBuilder().build(inputStream);
                }
            }
        }
        // 返回Session工厂
        return factory;
    }
}
  1. 测试 MyBatisUtil 工具类:
package mybatis;

/** @author 周航宇 */
public class MyBatisTest {

    /** 测试MyBatisUtil工具 */
    @SneakyThrows
    @Test
    public void myBatisUtil() {

        // 主配文件的位置: 在创建会话工厂的时侯,需要读取主配
        String filePath = "mybatis.xml";

        // 使用MyBatisUtil工具获取一个单例的sessionFactory
        SqlSessionFactory sessionFactory = MyBatisUtil.getFactory("mybatis.xml");

        // 通过SqlSessionFactory开启一个具有事务保护的session会话,用于和数据库打交道
        SqlSession sqlSession = sessionFactory.openSession();

        // 从SqlSession会话中获取一个连接
        Connection connection = sqlSession.getConnection();

        // 判断连接是否成功
        System.out.println(connection.isClosed() ? "连接失败" : "连接成功");

        // 关闭会话,清空一级缓存,可使用 `try-with-resource` 结构
        sqlSession.close();
    }
}

E02. MyBatis实体设计

1. ORM实体类概念

心法:ORM 全称 Object Relation Mapping 对象关系映射,其中 Object 是指 Java 对象,而 Relation 是指关系型数据库,其核心目标是可以通过操作 Java 对象的方式来操作数据库。

ORM 映射关系如下

  1. 类名 class 映射表名 table:如 User 类映射 user 表。
  2. 成员属性名 field 映射表的字段名 column:如 realName 属性映射 real_name 字段。
  3. 成员属性类型 javaType 映射表的字段类型 jdbcType
    • 如 java.lang.Integer 类型映射 tinyint 或 int 类型。
    • 如 java.lang.Long 类型映射 bigint 类型。
    • 如 java.lang.String 类型映射 varchar 或 char 类型。
    • 如 java.lang.LocalDateTime 类型映射 datetime 类型。
  4. 类实例 instance 映射表记录 record:如 user 实例映射 row 行。

2. ORM实体类分层

心法:ORM 实体类分层设计方案

在这里插入图片描述

3. ORM实体类开发

心法:ORM 实体类设计原则一共有 4 条。

  1. 类需要使用 implements Serializable 实现序列化标记接口。
  2. 类中的属性不要使用基本数据类型。
  3. 类中的属性必须要有 Setter/Getter 方法,可以直接标记 @Data 注解。
  4. 类建议重写 toString()/equals()/hashCode() 等方法。

武技:开发对应 MyBatis 库中全部表的 ORM 实体类

1.开发 ORM 实体类:

package com.joezhou.entity;

/** @author 周航宇 */
@Data
public class Student implements Serializable {    
    private Long id;
    private String realName;
    private Integer gender;
    private String phone;
    private Long fkClazzId;
    private LocalDateTime created;
    private LocalDateTime updated;
}
package com.joezhou.entity;

/** @author 周航宇 */
@Data
public class Clazz implements Serializable {
    private Long id;
    private String clazzName;
    private LocalDateTime created;
    private LocalDateTime updated;
}
package com.joezhou.entity;

/** @author 周航宇 */
@Data
public class Department implements Serializable {
    private Long id;
    private String dname;
    private LocalDateTime created;
    private LocalDateTime updated;
}
package com.joezhou.entity;

/** @author 周航宇 */  
@Data  
public class Employee implements Serializable {  
    private Long id;  
    private String ename;  
    private String phone;  
    private Long fkDepartmentId;  
    private LocalDateTime created;  
    private LocalDateTime updated;  
}
  1. 在 MyBatis 主配中设置下划线转驼峰:
<!--全局配置-->
<settings>
    <!--下划线转小驼峰-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

4. ORM实体类别名

心法:MyBatis 支持使用别名代替实体类的类全名,提升编码体验,同时也内置了一些简单常用的数据类型的别名。

MyBatis 内置别名如下

在这里插入图片描述

武技:在 MyBatis 主配中设置全部实体类的别名

<!--整包起别名: 别名不区分大小写-->
<typeAliases>
    <package name="com.joezhou.entity"/>
</typeAliases>

E03. MyBatis接口开发

心法:MyBatis 框架中,用于和数据库进行数据交互的数据层分为 Mapper 接口和 Mapper 配置两部分。

在这里插入图片描述

1. 开发Mapper接口

心法:Mapper 接口是一个 Java文件,用于编写接口方法并被上层(业务层)调用。

武技:开发数据层 Java 代码

  1. 开发全部表的 Mapper 接口:
package com.joezhou.mapper;

/** @author 周航宇 */
public interface ClazzMapper {
}
package com.joezhou.mapper;

/** @author 周航宇 */
public interface StudentMapper {
}
package com.joezhou.mapper;

/** @author 周航宇 */
public interface DepartmentMapper {
}
package com.joezhou.mapper;

/** @author 周航宇 */
public interface EmployeeMapper {
}

2. 开发Mapper配置

心法:Mapper 配置文件是一个 XML 文件,用于开发和配置 SQL 语句。

Mapper 配置文件要求

  1. Mapper 配置文件默认必须和 Mapper 接口同名同包。
  2. Mapper 配置文件中必须使用 <mapper namespace=""> 指向对应的接口。
  3. Mapper 配置文件所在的目录必须一层一层创建,而不能一次性创建。

武技:开发数据层 XML 代码

  1. 开发全部表的 Mapper 配置:
<!--com/joezhou/mapper/ClazzMapper.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">

<!--namespace 必须指向对应接口类全名-->
<mapper namespace="com.joezhou.mapper.ClazzMapper">

</mapper>
<!--com/joezhou/mapper/StudentMapper.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">

<!--namespace 必须指向对应接口类全名-->
<mapper namespace="com.joezhou.mapper.StudentMapper">

</mapper>
<!--com/joezhou/mapper/DepartmentMapper.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">

<!--namespace 必须指向对应接口类全名-->
<mapper namespace="com.joezhou.mapper.DepartmentMapper">

</mapper>
<!--com/joezhou/mapper/EmployeeMapper.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">

<!--namespace 必须指向对应接口类全名-->
<mapper namespace="com.joezhou.mapper.EmployeeMapper">

</mapper>

3. 扫描Mapper接口

武技:如果 Mapper 接口不被扫描,程序是无法发现它的。

  1. 在 MyBatis 主配文件中扫描 Mapper 接口:
<!--全局配置-->
<settings>
    <!--控制台打印SQL语句-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--接口扫描-->
<mappers>
    <!--在Mapper接口和配置文件同名同包时,整包扫描Mapper接口-->
    <package name="com.joezhou.mapper"/>
</mappers>

S02. MyBatis单表操作

心法:MyBatis 中的 DML 语句块分别使用 <insert><update><delete> 标签进行编写,后文统称为 DML 标签。

DML 标签常用属性如下

属性对应接口方法的是否可以省略
id方法名不可省略
parameterType参数类型或别名可以省略
resultType返回值类型或别名DML 操作必须省略,默认返回 int 型值表示影响条目数

E01. MyBatis单表添加

心法:MyBatis 中的占位符和拼接符都用于从实体类型的参数中获取指定的属性值。

MyBatis 支持 占位符拼接符 两种写法,区别如下:

符号#{ xx }${ xx }
中文占位符拼接符
是否支持连调支持,如 #{user.parent.name}支持,如 #{user.parent.name}
是否补充单引号自动补单引号,没有注入漏洞不会补单引号,存在注入漏洞

武技:开发 Clazz 表的单表添加业务

  1. 开发 Mapper 接口:
package com.joezhou.mapper;

/** @author 周航宇 */
public interface ClazzMapper {

    /**
     * 添加一条班级记录
     *
     * @param clazz 班级实体
     * @return 影响条目数
     */
    int insert(Clazz clazz);
}
  1. 开发 Mapper 配置:
<!--com/joezhou/mapper/ClazzMapper.xml-->

<!--添加一条班级记录-->
<!--useGeneratedKeys="true" 开启主键回注功能-->
<!--keyProperty="id" 将主键回注到实体类参数的 id 字段中-->
<insert id="insert" parameterType="Clazz"
		useGeneratedKeys="true" keyProperty="id">
	insert into clazz 
		(clazz_name, created, updated)
	values 
		(#{clazzName}, #{created}, #{updated})
</insert>
  1. 测试 Mapper 接口:
package mapper;

/** @author 周航宇 */
public class ClazzMapperTest {
  
	@Test
    public void insert() {
        
		// 使用 MyBatisUtil 工具获取会话工厂,并通过会话工厂获取一个会话
		SqlSession sqlSession = MyBatisUtil.getFactory("mybatis.xml").openSession();
		
		// 从会话中获取Mapper接口(因为主配中已经扫描了所有Mapper接口,所以可以直接获取)
		ClazzMapper clazzMapper = sqlSession.getMapper(ClazzMapper.class);
		
		// 准备参数
		Clazz clazz = new Clazz();
		clazz.setClazzName("J12141");
		clazz.setCreated(LocalDateTime.now());
		clazz.setUpdated(LocalDateTime.now());
		
		// 调用Mapper接口方法,进行添加操作(DML操作需要手动提交事务)
		System.out.println(clazzMapper.insert(clazz));
		sqlSession.commit();
		
		// 测试主键是否自动回注
		System.out.println(clazz);
		
		// 关闭会话,释放资源
		sqlSession.close();
    }
}

E02. MyBatis单表修改

武技:开发 Clazz 表的单表修改业务(按主键修改)

  1. 开发 Mapper 接口:
package com.joezhou.mapper;

/**  
 * 按主键修改一条班级记录  
 *  
 * @param clazz 班级实体  
 * @return 影响条目数  
 */  
int update(Clazz clazz);
  1. 开发 Mapper 配置:
<!--com/joezhou/mapper/ClazzMapper.xml-->

<!--按主键修改一条班级记录-->
<update id="update">
    update clazz set
        clazz_name = #{clazzName},
        created = #{created},
        updated = #{updated}
    where id = #{id}
</update>
  1. 测试 Mapper 接口:
package mapper;

/** @author 周航宇 */
public class ClazzMapperTest {

	@Test
	public void update() {
	
	    // 使用 MyBatisUtil 工具获取会话工厂,并通过会话工厂获取一个会话
	    SqlSession sqlSession = MyBatisUtil.getFactory("mybatis.xml").openSession();
	
	    // 从会话中获取Mapper接口(因为主配中已经扫描了所有Mapper接口,所以可以直接获取)
	    ClazzMapper clazzMapper = sqlSession.getMapper(ClazzMapper.class);
	
	    // 准备参数
	    Clazz clazz = new Clazz();
	    clazz.setId(10L);
	    clazz.setClazzName("05245");
	    clazz.setUpdated(LocalDateTime.now());
	    clazz.setCreated(LocalDateTime.now());
	
	    // 调用Mapper接口方法,进行修改操作(DML操作需要手动提交事务)
	    System.out.println(clazzMapper.update(clazz));
	    sqlSession.commit();
	
	    // 关闭会话,释放资源
	    sqlSession.close();
	}
}

1. 使用动态SQL优化

武技:单表修改相关推荐动态标签

where 标签:生成 WHERE 前缀关键字并删除内容中第一个 AND 或 OR 关键字:

<!--等价于 <trim prefix="WHERE", prefixOverrides="AND"> 写法-->
<where>
	内容
</where>

if 标签:仅在条件满足 OGNL 表达式时拼接指定 SQL 内容。

<if test="OGNL">
	内容
</if>

choose 标签

  • 条件满足 OGNL 表达式时拼接对应的 when 内容(支持编写多个)。
  • 条件不满足 OGNL 表达式时拼接 otherwise 内容(只能编写一个)。
<choose>
	<when test="OGNL">内容</when>
	<when test="OGNL">内容</when>
	<otherwise>内容</othersise>
</choose>

set 标签:生成 SET 前缀关键字并删除内容中最后一个英文逗号:

<!--等价于 <trim prefix="SET", suffixOverrides=","> 写法-->
<set>
	内容
</set>

武技:测试使用动态标签 set + if 优化单表修改的 SQL

<!--com/joezhou/mapper/ClazzMapper.xml-->

<!--按主键修改一条班级记录-->
<update id="update">
    update clazz
    <set>
        <if test="clazzName != null">clazz_name = #{clazzName},</if>
        <if test="created != null">created = #{created},</if>
        <if test="updated != null">updated = #{updated},</if>
    </set>
    where id = #{id}
</update>

E03. MyBatis单表删除

心法:当接口参数是简单类型如 int, String 等,则 OGNL 表达式中推荐使用 _parameter 获取该参数,SQL语句中使用 #{param1}/#{arg0} 来获取该接口方法中的第 1 个或第 2 个参数。

@Param 别名:MyBatis支持使用 @Param 对接口参数起别名,但此方式在SQL块之间相互调用时会失效,不推荐。

武技:开发 Clazz 表的单表删除方法(按主键删除)

  1. 开发 Mapper 接口:
package com.joezhou.mapper;

/**
 * 按主键删除班级记录
 * 
 * @param id 班级表主键
 * @return 影响条目数
 */
int delete(Long id);
  1. 开发 Mapper 配置:
<!--com/joezhou/mapper/ClazzMapper.xml-->

<!--按主键删除一条班级记录-->
<delete id="delete">
	delete from clazz where id = #{param1}
</delete>
  1. 测试 Mapper 接口:
package mapper;

/** @author 周航宇 */
public class ClazzMapperTest {

    @Test
    public void delete() {

        // 使用 MyBatisUtil 工具获取会话工厂,并通过会话工厂获取一个会话
        SqlSession sqlSession = MyBatisUtil.getFactory("mybatis.xml").openSession();
        // 从会话中获取Mapper接口(因为主配中已经扫描了所有Mapper接口,所以可以直接获取)
        ClazzMapper clazzMapper = sqlSession.getMapper(ClazzMapper.class);

        // 调用Mapper接口方法,进行删除操作(DML操作需要手动提交事务)
        System.out.println(clazzMapper.delete(9L));
        sqlSession.commit();

        // 关闭会话,释放资源
        sqlSession.close();
    }
}

E04. MyBatis批量删除

心法:批量删除业务中,建议使用动态标签 <foreach> 遍历入参并拼接到 SQL 中。

foreach 标签常用属性如下

<foreach collection="">:被遍历的元素,支持数组,列表,数组或列表类型的实体属性或 Map 的 key 值等:

  • 若被遍历的元素是一个数组,则固定为 array 关键字。
  • 若被遍历的元素是一个列表,则固定为 list 关键字。
  • 多个参数时替换为 arg0 写法,也可以使用别名。

<foreach item="">:设置循环中间变量,如 e,之后在循环内部可以配合使用 ${e} 将其取出。

<foreach open="">:设置循环开始时拼接的字符串 SQL 代码。

<foreach close="">:设置循环结束时拼接的字符串 SQL 代码。

<foreach separator="">:设置拼接符,自动忽略 SQL 语句中多余的拼接符,如最后一个逗号。
|

武技:开发 Clazz 表的批量删除方法(按主键列表删除)

  1. 开发Mapper接口
package com.joezhou.mapper;

public interface ClazzMapper {

    /**
     * 按主键列表批量删除班级记录
     *
     * @param ids 班级主键列表
     * @return 影响条目数
     */
    int deleteBatch(List<Long> ids);
}
  1. 开发 Mapper 配置:
<!--com/joezhou/mapper/ClazzMapper.xml-->

<!--按主键列表批删班级记录-->
<delete id="deleteBatch">
	delete from clazz where
	<foreach collection="list" item="e" open="id in (" close=")" separator=",">
		${e}
	</foreach>
</delete>
  1. 测试 Mapper 接口:
package mapper;

/** @author 周航宇 */
public class ClazzMapperTest {

	@Test
	public void deleteBatch() {
	
	    // 使用 MyBatisUtil 工具获取会话工厂,并通过会话工厂获取一个会话
	    SqlSession sqlSession = MyBatisUtil.getFactory("mybatis.xml").openSession();
	    // 从会话中获取Mapper接口(因为主配中已经扫描了所有Mapper接口,所以可以直接获取)
	    ClazzMapper clazzMapper = sqlSession.getMapper(ClazzMapper.class);
	
	    // 调用Mapper接口方法,进行批量删除操作(DML操作需要手动提交事务)
	    System.out.println(clazzMapper.deleteBatch(List.of(1L, 3L, 5L)));
	    sqlSession.commit();
	
	    // 关闭会话,释放资源
	    sqlSession.close();
	}
}

E05. MyBatis批量添加

武技:开发 Clazz 表的批量添加方法

  1. 开发 Mapper 接口:
package com.joezhou.mapper;

public interface ClazzMapper {

    /**
     * 批量添加班级记录
     *
     * @param clazzes 班级记录列表
     * @return 影响条目数
     */
    int insertBatch(List<Clazz> clazzes);
}
  1. 开发 Mapper 配置:
<!--com/joezhou/mapper/ClazzMapper.xml-->

<!--批量添加班级记录-->
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
	INSERT INTO clazz (clazz_name, created, updated)
	VALUES
	<foreach collection="list" item="clazz" separator=",">
		(#{clazz.clazzName}, #{clazz.created}, #{clazz.updated})
	</foreach>
</insert>
  1. 测试 Mapper 接口:
package mapper;

/** @author 周航宇 */
public class ClazzMapperTest {

	@Test
    public void insertBatch() {

        // 使用 MyBatisUtil 工具获取会话工厂,并通过会话工厂获取一个会话
        SqlSession sqlSession = MyBatisUtil.getFactory("mybatis.xml").openSession();
        // 从会话中获取Mapper接口(因为主配中已经扫描了所有Mapper接口,所以可以直接获取)
        ClazzMapper clazzMapper = sqlSession.getMapper(ClazzMapper.class);

        // 准备参数
        Clazz clazz1 = new Clazz();
        clazz1.setClazzName("J0001");
        clazz1.setCreated(LocalDateTime.now());
        clazz1.setUpdated(LocalDateTime.now());

        Clazz clazz2 = new Clazz();
        clazz2.setClazzName("J0002");
        clazz2.setCreated(LocalDateTime.now());
        clazz2.setUpdated(LocalDateTime.now());

        // 调用Mapper接口方法,进行批量添加操作(DML操作需要手动提交事务)
        System.out.println(clazzMapper.insertBatch(List.of(clazz1, clazz2)));
        sqlSession.commit();

        // 测试主键是否自动回注
        System.out.println(clazz1);
        System.out.println(clazz2);

        // 关闭会话,释放资源
        sqlSession.close();
    }
}

E06. MyBatis批量修改

心法:MyBatis 批量修改业务需要在 db.properties -> url 串的末尾拼接 ?allowMultiQueries=true 参数,拼接后,方可在SQL语句后面携带 ; 以实现多语句执行。

武技:开发 Clazz 表的批量修改方法

  1. 开发 Mapper 接口:
package com.joezhou.mapper;

public interface ClazzMapper {

    /**
     * 批量修改班级记录
     *
     * @param clazzes 班级记录列表
     * @return 影响条目数
     */
	int updateBatch(List<Clazz> clazzes);
}
  1. 开发 Mapper 配置:语句末尾必须使用 ; 进行分割:
<!--com/joezhou/mapper/ClazzMapper.xml-->

<!--批量修改班级记录-->
<update id="updateBatch">
    <foreach collection="list" item="clazz">
        UPDATE clazz
        <set>
            <if test="clazz.clazzName != null">
                clazz_name = #{clazz.clazzName},
            </if>
            <if test="clazz.created != null">
                created = #{clazz.created},
            </if>
            <if test="clazz.updated != null">
                updated = #{clazz.updated},
            </if>
        </set>
        WHERE id = #{clazz.id};
    </foreach>
</update>
  1. 测试 Mapper 接口:
package mapper;

/** @author 周航宇 */
public class ClazzMapperTest {

	@Test
    public void updateBatch() {

        // 使用 MyBatisUtil 工具获取会话工厂,并通过会话工厂获取一个会话
        SqlSession sqlSession = MyBatisUtil.getFactory("mybatis.xml").openSession();
        // 从会话中获取Mapper接口(因为主配中已经扫描了所有Mapper接口,所以可以直接获取)
        ClazzMapper clazzMapper = sqlSession.getMapper(ClazzMapper.class);

        // 准备参数
        Clazz clazz1 = new Clazz();
        clazz1.setId(1L);
        clazz1.setClazzName("J0001-1");
        clazz1.setCreated(LocalDateTime.now());
        clazz1.setUpdated(LocalDateTime.now());

        Clazz clazz2 = new Clazz();
        clazz2.setId(2L);
        clazz2.setClazzName("J0002-2");
        clazz2.setCreated(LocalDateTime.now());
        clazz2.setUpdated(LocalDateTime.now());

        // 调用Mapper接口方法,进行批量修改操作(DML操作需要手动提交事务)
        System.out.println(clazzMapper.updateBatch(List.of(clazz1, clazz2)));
        sqlSession.commit();

        // 关闭会话,释放资源
        sqlSession.close();
    }
}

Java道经第3卷 - 第1阶 - MyBatis(一)


传送门:JB3-1-MyBatis(一)
传送门:JB3-1-MyBatis(二)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值