MyBatis快速入门(中)

MyBatis快速入门(中)


官方文档


四、全局配置文件configuration标签中子标签顺序

mybatis全局配置文件mybatis-config.xml中的 configuration 标签中可以配置各种功能的子标签,但是这些子标签是有顺序要求的,确保配置文件结构正确,且每个标签都按照MyBatis规范的顺序放置,这样可以避免配置错误。

1、子标签顺序


2、子标签说明


  • <properties> :用于加载外部文件,包含 数据库连接信息,避免配置直接在配置文件中硬编码。

  • <settings>全局设置,用于控制MyBatis的行为,比如 是否启用驼峰配置、延迟加载和缓存

  • <typeAliases>定义类型别名,简化类的引用。例如,User别名代表com.example.model.User类。

  • <typeHandlers>配置自定义类型处理器,用于特殊类型的转换。

  • <objectFactory>自定义对象工厂,用于创建对象的自定义逻辑。

  • <objectWrapperFactory>自定义对象包装工厂,用于处理复杂对象的包装。

  • <reflectorFactory>自定义引用工厂,用于创建引用对象的工厂。

  • <plugins>插件配置,用于拦截SQL执行、结果映射等。

  • <environments>环境配置,用于定义不同的数据库环境,例如开发环境、测试环境等。

  • <databaseIdProvider> :数据库ID提供器,用于根据不同的数据库类型切换配置。

  • <mappers> :定义Mapper配置,可以是XML文件路径或Mapper接口所在的包路径。


3、<properties> 标签和 <environments> 标签详述


MyBatis 中,<properties> 标签和 <environments> 标签都可以包含数据库连接信息,但它们的使用目的和方式有所不同:


假设有一个database.properties文件,不同环境的数据库连接信息分别如下:

properties 复制代码
jdbc.driver=com.mysql.cj.jdbc.Driver

# 开发环境
dev.jdbc.url=jdbc:mysql://localhost:3306/dev_db
dev.jdbc.username=dev_user
dev.jdbc.password=dev_password

# 测试环境
test.jdbc.url=jdbc:mysql://localhost:3306/test_db
test.jdbc.username=test_user
test.jdbc.password=test_password

# 生产环境
prod.jdbc.url=jdbc:mysql://localhost:3306/prod_db
prod.jdbc.username=prod_user
prod.jdbc.password=prod_password

MyBatis配置文件中使用<properties>加载这些属性,再通过<environments>使用这些属性值:

xml 复制代码
<?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>
    <!-- 加载外部的数据库属性文件 -->
    <properties resource="database.properties"/>

    <!-- 配置不同的数据库环境 -->
    <environments default="development">
        
        <!-- 开发环境 -->
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${dev.jdbc.url}"/>
                <property name="username" value="${dev.jdbc.username}"/>
                <property name="password" value="${dev.jdbc.password}"/>
            </dataSource>
        </environment>

        <!-- 测试环境 -->
        <environment id="testing">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${test.jdbc.url}"/>
                <property name="username" value="${test.jdbc.username}"/>
                <property name="password" value="${test.jdbc.password}"/>
            </dataSource>
        </environment>

        <!-- 生产环境 -->
        <environment id="production">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${prod.jdbc.url}"/>
                <property name="username" value="${prod.jdbc.username}"/>
                <property name="password" value="${prod.jdbc.password}"/>
            </dataSource>
        </environment>

    </environments>

    <!-- 其他配置项 -->
</configuration>

4、配置文件代码示例

xml 复制代码
<?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>
    
    <!-- 1. properties: 配置属性,可选 -->
    <properties resource="database.properties">
        <property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mydatabase"/>
        <property name="jdbc.username" value="root"/>
        <property name="jdbc.password" value="password"/>
    </properties>

    <!-- 2. settings: 全局设置,可选 -->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <!-- 3. typeAliases: 类型别名定义,可选 -->
    <typeAliases>
        <typeAlias alias="User" type="com.example.model.User"/>
    </typeAliases>

    <!-- 4. typeHandlers: 自定义类型处理器配置,可选 -->
    <typeHandlers>
        <typeHandler handler="com.example.typehandler.CustomTypeHandler"/>
    </typeHandlers>

    <!-- 5. objectFactory: 自定义对象工厂配置,可选 -->
    <objectFactory type="com.example.factory.CustomObjectFactory">
        <property name="someProperty" value="someValue"/>
    </objectFactory>

    <!-- 6. objectWrapperFactory: 自定义对象包装工厂,可选 -->
    <objectWrapperFactory type="com.example.factory.CustomObjectWrapperFactory"/>

    <!-- 7. reflectorFactory: 自定义反射工厂配置,可选 -->
    <reflectorFactory type="com.example.factory.CustomReflectorFactory"/>

    <!-- 8. plugins: 插件配置,可选 -->
    <plugins>
        <plugin interceptor="com.example.plugin.LoggingInterceptor"/>
    </plugins>

    <!-- 9. environments: 数据库环境配置,必选 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <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>

    <!-- 10. databaseIdProvider: 数据库 ID 提供器,可选 -->
    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
    </databaseIdProvider>

    <!-- 11. mappers: Mapper 配置,可选 -->
    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
        <package name="com.example.mapper"/>
    </mappers>

</configuration>

五、MyBatis 基础功能之 resultMap

MyBatis中,resultMap是一个核心功能,用于将SQL查询结果映射到Java对象上。它提供了一种灵活的方式,将数据库表的字段Java类的属性进行关联,并能够处理复杂的映射需求。本章将通过具体案例来详细展示 resultMap 灵活的映射功能

1、建表语句

sql 复制代码
-- 创建 t_dept 表
CREATE TABLE t_dept (
    did INT(11) PRIMARY KEY AUTO_INCREMENT,
    dept_name VARCHAR(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;

-- 创建 t_emp 表
CREATE TABLE t_emp (
    eid INT(11) PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20) NOT NULL,
    age INT(11),
    sex CHAR(1),
    email VARCHAR(20),
    did INT(11),
    FOREIGN KEY (did) REFERENCES t_dept(did)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;


-- 向 t_dept 表插入 4 个部门数据
INSERT INTO t_dept (dept_name) VALUES ('Human Resources');
INSERT INTO t_dept (dept_name) VALUES ('Finance');
INSERT INTO t_dept (dept_name) VALUES ('Engineering');
INSERT INTO t_dept (dept_name) VALUES ('Marketing');

-- 向 t_emp 表插入 20 个员工数据
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Alice', 30, 'F', 'alice@example.com', 1);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Bob', 25, 'M', 'bob@example.com', 2);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Charlie', 28, 'M', 'charlie@example.com', 3);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Diana', 32, 'F', 'diana@example.com', 4);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Eve', 27, 'F', 'eve@example.com', 3);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Frank', 35, 'M', 'frank@example.com', 1);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('George', 40, 'M', 'george@example.com', 2);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Hannah', 29, 'F', 'hannah@example.com', 4);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Ivan', 31, 'M', 'ivan@example.com', 3);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Julia', 26, 'F', 'julia@example.com', 1);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Kevin', 33, 'M', 'kevin@example.com', 2);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Lily', 22, 'F', 'lily@example.com', 4);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Mike', 37, 'M', 'mike@example.com', 1);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Nina', 28, 'F', 'nina@example.com', 2);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Oscar', 45, 'M', 'oscar@example.com', 3);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Paula', 34, 'F', 'paula@example.com', 4);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Quinn', 30, 'M', 'quinn@example.com', 1);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Rachel', 27, 'F', 'rachel@example.com', 2);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Steve', 39, 'M', 'steve@example.com', 3);
INSERT INTO t_emp (emp_name, age, sex, email, did) VALUES ('Tina', 32, 'F', 'tina@example.com', 4);




-- 将所有男性的 sex 字段更新为 "男"
UPDATE t_emp SET sex = '男' WHERE sex = 'M';

-- 将所有女性的 sex 字段更新为 "女"
UPDATE t_emp SET sex = '女' WHERE sex = 'F';

2、解决表中字段名和类中属性名不一致



(1)配置 驼峰命名转换

如果表字段Java 类属性之间存在一致的映射规则(例如下划线驼峰命名规则),可以使用 MyBatis 提供的自动映射功能,使用settings标签在全局配置文件中设置,即配置 驼峰命名转换

xml 复制代码
<configuration>
    <settings>
        <!-- 开启驼峰命名转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

(2)使用别名 AS 配合字段映射

也可以在SQL查询中使用别名,使字段名属性名相匹配:

xml 复制代码
    <!--查询所有员工信息-->
    <select id="getAllEmp" resultType="Emp">
        select eid, emp_name as empName,age,sex,email,did from t_emp
    </select>

(3)使用 @Results 和 @Result 注解

在映射 SQL 语句时,可以通过 @Results 注解手动指定 字段属性之间的对应关,通过注解可以完全避免使用XML文件
但是这种方式仅适用于简单查询,对于复杂的查询需求,例如动态 SQL批量插入等,建议使用 XML 映射文件,因为 XML 支持更灵活的 SQL 语句构建。

如下示例中,在mapper接口方法中 ,emp_name 数据库字段被映射到 empName 属性,此时就不再需要 EmpMapper.xml映射文件了

java 复制代码
public interface EmpMapper {
    /**
     * 查询所有员工信息
     */
    @Select("SELECT * FROM t_emp")
    @Results({
            @Result(property = "empName", column = "emp_name")
    })
    List<Emp> getAllEmp();
}

(4)使用 Map 类型

如果想要定义明确的类,可以将结果映射到Map类型,这样数据库字段名会直接作为Map存储的键值。

如下示例中,在mapper接口方法中 ,数据库字段名会直接作为Map存储的键值,此时就不再需要 EmpMapper.xml映射文件了

java 复制代码
public interface EmpMapper {
    /**
     * 查询所有员工信息
     */
    @Select("SELECT * FROM t_emp")
    List<Map<String, Object>> getAllEmp();
}

注意测试方法接收数据类型

java 复制代码
public class EmpMapperTest {
    /**
     * 查询所有员工信息
     */
    @Test
    public void testGetAllEmp() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
        List<Map<String, Object>> empList = empMapper.getAllEmp();
        empList.forEach(System.out::println);
    }
}

(5)使用 <resultMap> 配置全局映射

如果需要更灵活的字段到属性的映射控制,可以在配置文件中通过 <resultMap> 配置全局映射。在 mapper 接口的 XML 映射文件中,可以通过 <resultMap> 手动映射数据库字段和类属性。创建一个通用的映射规则,适用于多个查询。

例如,在 EmpMapper.xml 中定义一个 <resultMap>,然后在其他查询中引用这个 resultMap:例如:

sql 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.EmpMapper">

 <!-- 定义一个通用的 resultMap -->
    <resultMap id="empResultMap" type="Emp">
        <!-- id 标签 用来设置主键的映射关系-->
        <id property="eid" column="eid"/>
        <!-- result 标签 用来设置普通字段的映射关系-->
        <result property="empName" column="emp_name"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
        <result property="did" column="did"/>
    </resultMap>

 <!-- 查询时使用 resultMap -->
    <select id="getAllEmp" resultMap="empResultMap">
        select * from t_emp
    </select>
    
</mapper>

3、resultMap 映射

(1)字段映射

resultMap可以将数据库的字段名Java类的属性名进行映射,特别是在字段名属性名不一致的情况下。

参考示例:本章内容 2-(5)

(2)多对一关联映射

中创建所对应的对象

  • (如下案例:在实体类Emp中创建Dept类型的属性对象)
    案例: 根据id 查询员工信息的时候,把员工所在的部门信息也查出来

  • 在这个场景中,一个员工属于一个部门,但一个部门可能有多个员工。员工(Employee)部门(Department)的关系是多对一的; 多个员工指向一个部门,因此这是典型的多对一关系。

  • 实体类
java 复制代码
package com.ningxia.mybatis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Integer eid;
    private String empName;
    private Integer age;
    private Character sex;
    private String email;
    private Integer did;
    private Dept dept;// 当关联的是一个对象时,这种多对一的关系,直接在'多'的一方中创建'一'所对应的对象
}

-----------------------------------------
package com.ningxia.mybatis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
    private Integer did;
    private String deptName;
}
(a)使用 AS 别名 (as 可省略)

可以使用多表联查,AS起别名的方式解决这种问题,使用 Map类型接收结果

  • EmpMapper 接口方法
java 复制代码
/**
 * 根据员工id查询员工信息和员工所在部门信息
 * <p>
 * 分步查询 根据员工id查询员工信息和员工所在部门信息
 * 1、 先根据员工id查询员工信息
 * 2、 再根据员工信息中的部门id去获取对应的部门信息
 */
Map<String, Object> getEmpAndDeptByIdOne(@Param("id") Integer id);
  • EmpMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.EmpMapper">

    <!--根据员工id查询员工信息和员工所在部门信息 -->
    <!--方式一:多表关联 使用 AS 起别名-->
    <select id="getEmpAndDeptByIdOne" resultType="Map">
        select
        e.eid,
        e.emp_name empName,
        e.age,
        e.sex,
        e.email,
        e.did eDid,
        d.did dDid,
        d.dept_name deptName
        from t_emp e
        left join t_dept d
        on e.did = d.did
        where e.eid = #{id}
    </select>

</mapper>
  • 测试类方法
java 复制代码
package com.ningxia.mybatis.test;

import com.ningxia.mybatis.mapper.EmpMapper;
import com.ningxia.mybatis.pojo.Emp;
import com.ningxia.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;
import java.util.Map;

public class EmpMapperTest {

    /**
     * 根据员工id查询员工信息和员工所在部门信息
     * <p>
     * 分步查询 根据员工id查询员工信息和员工所在部门信息
     * 1、 先根据员工id查询员工信息
     * 2、 再根据员工信息中的部门id去获取对应的部门信息
     */
    @Test
    public void testGetEmpAndDeptById() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
        Map<String, Object> emp1 = empMapper.getEmpAndDeptByIdOne(1);// 方式一
        System.out.println("***************************开始***************************" );
        System.out.println("方式一:"+emp1);
        System.out.println("***************************结束***************************" );
    }
}
  • 查询结果
(b)resultMap 级联属性赋值
  • EmpMapper 接口方法
java 复制代码
    /**
     * 根据员工id查询员工信息和员工所在部门信息
     * <p>
     * 分步查询 根据员工id查询员工信息和员工所在部门信息
     * 1、 先根据员工id查询员工信息
     * 2、 再根据员工信息中的部门id去获取对应的部门信息
     */
    Emp getEmpAndDeptByIdTwo(@Param("id") Integer id);
  • EmpMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.EmpMapper">

  <!--方式二:resultMap 级联属性赋值-->
    <select id="getEmpAndDeptByIdTwo" resultMap="empResultMapOne">
        select
        *
        from t_emp e
        left join t_dept d
        on e.did = d.did
        where e.eid = #{id}
    </select>

    <resultMap id="empResultMapOne" type="Emp">
        <!-- id 标签 用来设置主键的映射关系-->
        <id property="eid" column="eid"/>
        <!-- result 标签 用来设置普通字段的映射关系-->
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
        <result property="did" column="did"/>
        <!-- 将 t_dept 表中字段 did、dept_name的值分别
        映射到 Emp 类下的 dept对象的did、deptName属性上 -->
        <result property="dept.did" column="did"/>
        <result property="dept.deptName" column="dept_name"/>
    </resultMap>



</mapper>
  • 测试类方法
java 复制代码
package com.ningxia.mybatis.test;

import com.ningxia.mybatis.mapper.EmpMapper;
import com.ningxia.mybatis.pojo.Emp;
import com.ningxia.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;
import java.util.Map;

public class EmpMapperTest {

    /**
     * 根据员工id查询员工信息和员工所在部门信息
     * <p>
     * 分步查询 根据员工id查询员工信息和员工所在部门信息
     * 1、 先根据员工id查询员工信息
     * 2、 再根据员工信息中的部门id去获取对应的部门信息
     */
    @Test
    public void testGetEmpAndDeptById() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp2 = empMapper.getEmpAndDeptByIdTwo(1); // 方式二
        System.out.println("***************************开始***************************" );
        System.out.println("方式二:"+ emp2);
        System.out.println("***************************结束***************************" );
    }
}
  • 查询结果
(c)使用 association 标签定义关联的单个对象的封装

实体类、mapper接口方法、测试类和(b)中一样,只是映射文件中定义的 resultMap 不同

  • EmpMapper 接口方法
java 复制代码
    /**
     * 根据员工id查询员工信息和员工所在部门信息
     * <p>
     * 分步查询 根据员工id查询员工信息和员工所在部门信息
     * 1、 先根据员工id查询员工信息
     * 2、 再根据员工信息中的部门id去获取对应的部门信息
     */
    Emp getEmpAndDeptByIdThree(@Param("id") Integer id);
  • EmpMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.EmpMapper">

   <!--方式三:使用 association 标签定义关联的单个对象的封装-->
    <select id="getEmpAndDeptByIdThree" resultMap="empResultMapTwo">
        select
        *
        from t_emp e
        left join t_dept d
        on e.did = d.did
        where e.eid = #{id}
    </select>

    <resultMap id="empResultMapTwo" type="Emp">
        <!-- id 标签 用来设置主键的映射关系-->
        <id property="eid" column="eid"/>
        <!-- result 标签 用来设置普通字段的映射关系-->
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
        <result property="did" column="did"/>
        <!--使用 association标签 把 dept 对象单独封装起来
        association
            property:指定映射到实体类的对象属性(Emp类中的 dept 属性)
            javaType:指定映射到实体对象属性的类型(Emp类中的 dept 属性的类型)

        result  映射列
            property:属性名
            column:  表中字段名
        -->
        <association property="dept" javaType="Dept">
            <!-- id 标签 用来设置主键的映射关系-->
            <id property="did" column="did"/>
            <!-- result 标签 用来设置普通字段的映射关系-->
            <result property="deptName" column="dept_name"/>
        </association>
    </resultMap>


</mapper>
  • 测试类方法
java 复制代码
package com.ningxia.mybatis.test;

import com.ningxia.mybatis.mapper.EmpMapper;
import com.ningxia.mybatis.pojo.Emp;
import com.ningxia.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;
import java.util.Map;

public class EmpMapperTest {

    /**
     * 根据员工id查询员工信息和员工所在部门信息
     * <p>
     * 分步查询 根据员工id查询员工信息和员工所在部门信息
     * 1、 先根据员工id查询员工信息
     * 2、 再根据员工信息中的部门id去获取对应的部门信息
     */
    @Test
    public void testGetEmpAndDeptById() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp3 = empMapper.getEmpAndDeptByIdThree(1); // 方式三
        System.out.println("***************************开始***************************" );
        System.out.println("方式三:"+emp3);
        System.out.println("***************************结束***************************" );
    }
}
  • 查询结果
(d)association 分段查询
  • EmpMapper 接口方法
java 复制代码
    /**
     * 根据员工id查询员工信息和员工所在部门信息
     * <p>
     * 分步查询 根据员工id查询员工信息和员工所在部门信息
     * 1、 先根据员工id查询员工信息
     * 2、 再根据员工信息中的部门id去获取对应的部门信息
     */
    Emp getEmpAndDeptByIdFour(@Param("id") Integer id);
  • EmpMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.EmpMapper">

    <!--方式四:使用 association 分段查询
        分步查询 根据员工id查询员工信息和员工所在部门信息
        1、先根据员工id查询员工信息
        2、再根据员工信息中的部门id去获取对应的部门信息
    -->
    <select id="getEmpAndDeptByIdFour" resultMap="empInfoByStepMap">
        select * from t_emp where eid = #{id}
    </select>

    <!--定义一个 resultMap -->
    <resultMap id="empInfoByStepMap" type="Emp">
        <!-- id 标签 用来设置主键的映射关系-->
        <id property="eid" column="eid"/>
        <!-- result 标签 用来设置普通字段的映射关系-->
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
        <result property="did" column="did"/>
        <!--使用 association标签 把 dept 对象单独封装起来
            association
                property:指定映射到实体类的对象属性(Emp类中的 dept 属性)
                javaType:指定映射到实体对象属性的类型(Emp类中的 dept 属性的类型)
                column:指定表中对应的字段(即查询返回的列名,用该字段去关联查询)
                select:指定引入嵌套查询的子SQL语句的id值(即查询员工对应部门信息的sql id值 getEmpAndDeptByIdStepTwo)

            result  映射列
                property:属性名
                column:  表中字段名
        -->
        <association property="dept" javaType="Dept" column="did" select="getEmpAndDeptByIdStepTwo">
            <!-- id 标签 用来设置主键的映射关系-->
            <id property="did" column="did"/>
            <!-- result 标签 用来设置普通字段的映射关系-->
            <result property="deptName" column="dept_Name"/>
        </association>
    </resultMap>

    <select id="getEmpAndDeptByIdStepTwo" resultType="Dept">
        select * from t_dept where did = #{did}
    </select>

</mapper>
  • 测试类方法
java 复制代码
package com.ningxia.mybatis.test;

import com.ningxia.mybatis.mapper.EmpMapper;
import com.ningxia.mybatis.pojo.Emp;
import com.ningxia.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;
import java.util.Map;

public class EmpMapperTest {

    /**
     * 根据员工id查询员工信息和员工所在部门信息
     * <p>
     * 分步查询 根据员工id查询员工信息和员工所在部门信息
     * 1、 先根据员工id查询员工信息
     * 2、 再根据员工信息中的部门id去获取对应的部门信息
     */
    @Test
    public void testGetEmpAndDeptById() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp4 = empMapper.getEmpAndDeptByIdFour(1); // 方式四
        System.out.println("***************************开始***************************" );
        System.out.println("方式四:"+emp4);
        System.out.println("***************************结束***************************" );
    }
}
  • 查询结果

上述 分步查询所有SQL都是在映射文件 EmpMapper.xml中写的,也可以分开写,EmpDept的信息各自写在自己的映射文件中

  • EmpMapper 接口方法
java 复制代码
 /**
  * 根据员工id查询员工信息和员工所在部门信息
  * <p>
  * 分步查询 根据员工id查询员工信息和员工所在部门信息
  * 1、 先根据员工id查询员工信息
  * 2、 再根据员工信息中的部门id去获取对应的部门信息
  */
Emp getEmpAndDeptByIdFive(@Param("id") Integer id);
  • EmpMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.EmpMapper">

    <!--方式五:使用 association 分段查询  分开写
        分步查询 根据员工id查询员工信息和员工所在部门信息
        1、先根据员工id查询员工信息
        2、再根据员工信息中的部门id去获取对应的部门信息
    -->
    <select id="getEmpAndDeptByIdFive" resultMap="empInfo">
        select * from t_emp where eid = #{id}
    </select>

    <!--定义一个 resultMap -->
    <resultMap id="empInfo" type="Emp">
        <!-- id 标签 用来设置主键的映射关系-->
        <id property="eid" column="eid"/>
        <!-- result 标签 用来设置普通字段的映射关系-->
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
        <result property="did" column="did"/>
        <!--使用 association标签 把 dept 对象单独封装起来
            association
                property:指定映射到实体类的对象属性(Emp类中的 dept 属性)
                javaType:指定映射到实体对象属性的类型(Emp类中的 dept 属性的类型)
                column:指定表中对应的字段(即查询返回的列名,用该字段去关联查询)
                select:指定引入嵌套查询的子SQL语句的id值(即DeptMapper.xml中分步查询第二步的sql id值 getEmpAndDeptByIdStepTwo)
        -->
        <association property="dept"
                     javaType="Dept"
                     column="did"
                     select="com.ningxia.mybatis.mapper.DeptMapper.getEmpAndDeptInfoStepTwo">
        </association>
    </resultMap>

</mapper>
  • DeptMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.DeptMapper">

    <!-- 分步查询:
     根据员工id查询员工信息和员工所在部门信息
            1、先根据员工id查询员工信息
            2、再根据员工信息中的部门id去获取对应的部门信息

    第二步:2、再根据员工信息中的部门id去获取对应的部门信息
    -->
    <select id="getEmpAndDeptInfoStepTwo" resultType="Dept">
        select * from t_dept where did = #{did}
    </select>

</mapper>
  • 测试类方法
java 复制代码
package com.ningxia.mybatis.test;

import com.ningxia.mybatis.mapper.EmpMapper;
import com.ningxia.mybatis.pojo.Emp;
import com.ningxia.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;
import java.util.Map;

public class EmpMapperTest {

    /**
     * 根据员工id查询员工信息和员工所在部门信息
     * <p>
     * 分步查询 根据员工id查询员工信息和员工所在部门信息
     * 1、 先根据员工id查询员工信息
     * 2、 再根据员工信息中的部门id去获取对应的部门信息
     */
    @Test
    public void testGetEmpAndDeptById() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp5 = empMapper.getEmpAndDeptByIdFive(1); // 方式五
        System.out.println("***************************开始***************************");
        System.out.println("方式五:分段查询分开写" + emp5);
        System.out.println("***************************结束***************************");
    }
}
  • 测试类方法
(😀)分步查询之延迟加载(按需加载)
  • mybatis-config.xml 配置文件
xml 复制代码
<settings>
    <setting name="lazyLoadingEnabled" value="true" />
    <setting name="aggressiveLazyLoading" value="false" />
</settings>

lazyLoadingEnabled开启或关闭全局延时加载功能

  • true启用延时加载。 只有在需要访问某个关联属性时,才会触发查询以加载数据

  • false(默认值)关闭延时加载。 MyBatis 会在加载主对象时立即加载所有关联对象
    aggressiveLazyLoading控制延时加载的策略是否激进

  • true激进延时加载模式。 一旦访问对象的任意一个延时加载的属性,会立即加载所有其他延时属性。

  • false非激进延时加载模式。 只有在明确访问某个属性时,才会触发查询来加载该属性的数据,其他延时属性不会被加载。

  • <association> 标签中的 fetchType 属性

fetchType属性用于定义 MyBatis 关联查询的加载策略

  • lazy延时加载(Lazy Loading)
  • eager立即加载(Eager Loading)

如果 未显式指定 fetchType,会继承全局设置 lazyLoadingEnabled 的值

如果 指定了 fetchType,局部配置会覆盖全局设置



测试结果:



MyBatis 3.5.11 : 因此 aggressiveLazyLoading 默认值是 false



(😀)lazyLoadingEnabled 与 aggressiveLazyLoading

lazyLoadingEnabledaggressiveLazyLoading 是两个相辅相成但有不同作用的配置,它们的设计并不多余,而是用来满足不同的延迟加载场景需求。


sql 复制代码
<settings>
  <setting name="lazyLoadingEnabled" value="true" />
  <setting name="aggressiveLazyLoading" value="true" />
</settings>

访问代码:

sql 复制代码
Order order = orderMapper.selectById(1);
// 访问 customer 时
System.out.println(order.getCustomer().getName()); // 触发延迟加载,加载 customer 和 items。
  • 一次性加载了 customeritems,即使你只访问了 customer


(3)一对多关联映射

中创建所对应的集合

  • (如下案例:在实体类Dept中创建Emp对应的集合对象)
    案例 : 如果关联的是一个集合
    【如:查询部门信息的时候,把当前部门所有员工信息也查询出来】
  • 实体类
sql 复制代码
package com.ningxia.mybatis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
    private Integer did;
    private String deptName;
    private List<Emp> empList; // 在 Dept 中创建 Emp 对应的集合对象
}
(a)使用 collocation 处理一对多的映射关系

<collection>标签

  • 作用:表示多个关系,就是把查询出来的结果映射成一个"列表"属性。 此处就表示 这个属性(例如 empList)是一个装着"员工 Emp"的集合
  • property="empList"指定主对象中用于存储子对象集合的属性名 ,此处指 Dept 类中的 empList 属性。
  • ofType="Emp"指定子对象集合的类型 ,此处表示集合中的每个元素都是 Emp 类型。
  • DeptMapper 接口方法
java 复制代码
    /**
     * 获取部门及部门中所有员工信息
     */
    Dept getDeptAndEmpInfoById(@Param("id") Integer id)  ;
  • DeptMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.DeptMapper">
    <!--
       获取部门及部门中所有员工信息
        Dept getDeptAndEmpInfoById(@Param("id") Integer id)  ;
    -->
    <select id="getDeptAndEmpInfoById" resultMap="DeptAndEmpMap">
        select * from t_dept d left join t_emp e on d.did = e.did where d.did = #{id}
    </select>

    <resultMap id="DeptAndEmpMap" type="Dept">
        <!-- id 标签 用来设置主键的映射关系-->
        <id column="did" property="did"/>
        <!-- result 标签 用来设置普通字段的映射关系-->
        <result column="dept_name" property="deptName"/>

        <!--
            <collection>标签
            作用:表示多个关系,就是把查询出来的结果映射成一个"列表"属性。
            此处就表示 这个属性(例如 empList)是一个装着"员工 Emp"的集合
            property="empList":指定主对象中用于存储子对象集合的属性名,此处指 Dept 类中的 empList 属性。
            ofType="Emp":指定子对象集合的类型,此处表示集合中的每个元素都是 Emp 类型。
        -->
        <collection property="empList" ofType="Emp">
            <!-- id 标签 用来设置主键的映射关系-->
            <id column="eid" property="eid"/>
            <!-- result 标签 用来设置普通字段的映射关系-->
            <result column="emp_name" property="empName"/>
            <result column="age" property="age"/>
            <result column="sex" property="sex"/>
            <result column="email" property="email"/>
            <result column="did" property="did"/>
        </collection>
    </resultMap>

</mapper>
  • 测试类方法
java 复制代码
package com.ningxia.mybatis.test;

import com.ningxia.mybatis.mapper.DeptMapper;
import com.ningxia.mybatis.pojo.Dept;
import com.ningxia.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class DeptMapperTest {

    /**
     * 获取部门及部门中所有员工信息
     */
    @Test
    public void testGetDeptAndEmpInfoById() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
        Dept dept = deptMapper.getDeptAndEmpInfoById(3);
        System.out.println(dept);
    }
}
  • 查询结果
(b)使用 collocation 分步查询

下面示例中,分步查询都写在一块了,也可以分开写,分开写参考association 分段查询中分开写的示例,此处不在赘述

<collection>标签:

  • 作用:表示多个关系,就是把查询出来的结果映射成一个"列表"属性。
  • property :指定主对象中用于存储子对象集合的属性名
  • ofType:指定子对象集合的类型
  • column:指定表中对应的字段(即查询返回的列名,用该字段去关联查询),可以使用 column"{参数名1=列名1, 参数名2=列名2}" 的形式传递多列数据
  • select:指定引入嵌套查询的子SQL语句的id
  • fetchType="lazy|eager" : 延迟加载|立即加载;不显示写出来默认继承全局配置文件中的 lazyLoadingEnabled
  • DeptMapper 接口方法
java 复制代码
    /**
     * 获取部门及部门中所有员工信息
     * 分步查询
     */
    Dept getDeptAndEmpInfoByStep(@Param("id") Integer id)  ;
  • DeptMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.DeptMapper">
    <!--
       获取部门及部门中所有员工信息  分步查询
        Dept getDeptAndEmpInfoByStep(@Param("id") Integer id)  ;
    -->
    <select id="getDeptAndEmpInfoByStep" resultMap="DeptAndEmpByStepMap">
        select * from t_dept where did = #{id}
    </select>

    <resultMap id="DeptAndEmpByStepMap" type="Dept">
        <!-- id 标签 用来设置主键的映射关系-->
        <id column="did" property="did"/>
        <!-- result 标签 用来设置普通字段的映射关系-->
        <result column="dept_name" property="deptName"/>
        <!--
            <collection>标签
            作用:表示多个关系,就是把查询出来的结果映射成一个"列表"属性。
            此处就表示 这个属性(例如 empList)是一个装着"员工 Emp"的集合
            property="empList":指定主对象中用于存储子对象集合的属性名,此处指 Dept 类中的 empList 属性。
            ofType="Emp":指定子对象集合的类型,此处表示集合中的每个元素都是 Emp 类型。
            column:指定表中对应的字段(即查询返回的列名,用该字段去关联查询)
            select:指定引入嵌套查询的子SQL语句的id值(即查询员工对应部门信息的 sql id值 getDeptAndEmpInfoByStepTwo
            fetchType="lazy|eager" : 延迟加载|立即加载;不显示写出来默认继承全局配置文件中的 lazyLoadingEnabled 值
        -->
        <collection property="empList"
                    ofType="Emp"
                    column="did"
                    select="getDeptAndEmpInfoByStepTwo"
                    fetchType="lazy"
        >
        </collection>
    </resultMap>

    <!--分步查询第二步:根据did查询员工信息-->
    <select id="getDeptAndEmpInfoByStepTwo" resultType="Emp">
        select * from t_emp where did = #{id}
    </select>

</mapper>
  • 测试类方法
java 复制代码
package com.ningxia.mybatis.test;

import com.ningxia.mybatis.mapper.DeptMapper;
import com.ningxia.mybatis.pojo.Dept;
import com.ningxia.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class DeptMapperTest {

    /**
     * 获取部门及部门中所有员工信息 分步查询
     */
    @Test
    public void testGetDeptAndEmpInfoByStep() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
        Dept dept = deptMapper.getDeptAndEmpInfoByStep(3);
        System.out.println(dept.getDeptName());
    }
}
  • 查询结果

五、MyBatis 基础功能之动态 SQL

MyBatis动态 SQL 是其核心功能之一,用于根据条件动态生成 SQL 语句。

  • 动态SQL标签:

if

  • 根据标签中test属性所对应的表达式决定标签中的内容是否拼接到SQL中;
sql 复制代码
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp where
        <if test="empName != null and empName != ''">
            emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            and age = #{age}
        </if>
        <if test="sex != null and sex != ''">
            and sex = #{sex}
        </if>
        <if test="email != null and email != ''">
            and email = #{email}
        </if>
    </select>
  • 存在问题 :假如第一个条件为空,会造成 where 后面直接拼接 andSQL 语句错误导致查询失败

  • 解决办法:

  • where 后面拼接 1=1 这样的类似语句

  • 使用 <where>标签

where

  • where 标签中有内容时,会自动生成 where 关键字,并且将内容前多余的 andor 去掉;
  • where 标签中没有内容时,此时 where标签没有任何效果;

注意:where 标签 不能 将内容后面多余的 andor 去掉;

sql 复制代码
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <where>
            <if test="empName != null and empName != ''">
                 and emp_name = #{empName}
            </if>
            <if test="age != null and age != ''">
                and age = #{age}
            </if>
            <if test="sex != null and sex != ''">
                and sex = #{sex}
            </if>
            <if test="email != null and email != ''">
                and email = #{email}
            </if>
        </where>
    </select>
  • 存在问题where 标签 不能 将内容后面多余的 andor 去掉;
  • 解决办法:使用<trim>标签

trim

当标签中有内容时:

  • prefix :前缀,给拼串后的整个字符串加一个前缀
  • prefixOverrides :前缀覆盖,去掉整个字符串前面多余的字符串
  • suffix :后缀,给拼串后的整个字符串加一个后缀
  • suffixOverrides :后缀覆盖,去掉整个字符串后面多余的字符

当标签中没有内容时,trim标签也没有任何效果

sql 复制代码
<select id="getEmpByCondition" resultType="Emp">
    select * from t_emp
    <trim prefix="where" suffixOverrides="and|or">
        <if test="empName != null and empName != ''">
            emp_name = #{empName} and
        </if>
        <if test="age != null and age != ''">
            age = #{age} and
        </if>
        <if test="sex != null and sex != ''">
            sex = #{sex} and
        </if>
        <if test="email != null and email != ''">
            email = #{email}
        </if>
    </trim>
</select>
******************************
<select id="getEmpByCondition" resultType="Emp">
    select * from t_emp
    <trim prefix="where" prefixOverrides="and|or">
        <if test="empName != null and empName != ''">
            and emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            and age = #{age}
        </if>
        <if test="sex != null and sex != ''">
            or sex = #{sex}
        </if>
        <if test="email != null and email != ''">
            and email = #{email}
        </if>
    </trim>
</select>

choose...when...otherwise

  • choose标签 (when,otherwise): 分支选择 ,类似 if...else if...else 或带了breakswtich-case只会进入其中一个分支
  • when 至少有一个,otherwise最多有一个;
sql 复制代码
<select id="getEmpByCondition" resultType="Emp">
    select * from t_emp
    <where>
        <choose>
            <when test="empName != null and empName != ''">
                emp_name = #{empName}
            </when>
            <when test="age != null and age != ''">
                age = #{age}
            </when>
            <when test="sex != null and sex != ''">
                sex = #{sex}
            </when>
            <otherwise>
                sex = '女'
            </otherwise>
        </choose>
    </where>
</select>

foreach

  • collection: 设置需要循环的数组或集合
  • item: 表示数组或集合中的每一个对象
  • separator(可选) 循环体之间的分隔符
  • open(可选) foreach标签所循环的所有内容的开始符
  • close(可选) foreach标签所循环的所有内容的结束符
  • index(可选) 表示当前遍历的索引。在 <foreach> 标签体中,你可以使用 #{index} 来引用当前遍历到的索引;
  • 遍历 list 的时候,index就是索引,item就是当前值;
  • 遍历 map的时候,index表示的就是mapkeyitem就是map的值
  • DynamicMapper 接口方法
java 复制代码
    /**
     * 查询: 传递参数为一个数组
     */
    List<Emp> getEmpByArray(@Param("eids") Integer[] eids);

    /**
     * 通过list集合实现批量添加员工
     */
    Integer insertMoreEmpByList(@Param("empList") List<Emp> empList);

    /**
     * 通过list集合实现批量删除员工
     */
    Integer deleteMoreEmpByList(@Param("eids")  Integer[] eids);
  • DynamicMapper 映射文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningxia.mybatis.mapper.DynamicMapper">
    <!-- 多条件查询
        List<Emp> getEmpByCondition(Emp emp)

       1、if标签
        <select id="getEmpByCondition" resultType="Emp">
            select * from t_emp where 1 = 1
            <if test="empName != null and empName != ''">
                emp_name = #{empName}
            </if>
            <if test="age != null and age != ''">
                and age = #{age}
            </if>
            <if test="sex != null and sex != ''">
                and sex = #{sex}
            </if>
            <if test="email != null and email != ''">
                and email = #{email}
            </if>
        </select>

       2、where 标签
        <select id="getEmpByCondition" resultType="Emp">
            select * from t_emp
            <where>
                <if test="empName != null and empName != ''">
                    emp_name = #{empName}
                </if>
                <if test="age != null and age != ''">
                    and age = #{age}
                </if>
                <if test="sex != null and sex != ''">
                    and sex = #{sex}
                </if>
                <if test="email != null and email != ''">
                    and email = #{email}
                </if>
            </where>
        </select>

       3、trim 标签
        <select id="getEmpByCondition" resultType="Emp">
            select * from t_emp
            <trim prefix="where" suffixOverrides="and|or">
                <if test="empName != null and empName != ''">
                    emp_name = #{empName} and
                </if>
                <if test="age != null and age != ''">
                    age = #{age} and
                </if>
                <if test="sex != null and sex != ''">
                    sex = #{sex} and
                </if>
                <if test="email != null and email != ''">
                    email = #{email}
                </if>
            </trim>
        </select>
        ******************************
        <select id="getEmpByCondition" resultType="Emp">
            select * from t_emp
            <trim prefix="where" prefixOverrides="and|or">
                <if test="empName != null and empName != ''">
                    and emp_name = #{empName}
                </if>
                <if test="age != null and age != ''">
                    and age = #{age}
                </if>
                <if test="sex != null and sex != ''">
                    or sex = #{sex}
                </if>
                <if test="email != null and email != ''">
                    and email = #{email}
                </if>
            </trim>
        </select>

       4、choose...when...otherwise 标签
           <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <where>
            <choose>
                <when test="empName != null and empName != ''">
                    emp_name = #{empName}
                </when>
                <when test="age != null and age != ''">
                    age = #{age}
                </when>
                <when test="sex != null and sex != ''">
                    sex = #{sex}
                </when>
                <otherwise>
                    sex = '女'
                </otherwise>
            </choose>
        </where>
    -->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <where>
            <choose>
                <when test="empName != null and empName != ''">
                    emp_name = #{empName}
                </when>
                <when test="age != null and age != ''">
                    age = #{age}
                </when>
                <when test="sex != null and sex != ''">
                    sex = #{sex}
                </when>
                <otherwise>
                    sex = '女'
                </otherwise>
            </choose>
        </where>
    </select>


    <!--  查询: 传递参数为一个数组
        List<Emp>  getEmpByArray(@Param("eids") Integer[] eids)

        collection: 设置需要循环的数组或集合;
        item: 表示数组或集合中的每一个对象;
        separator:(可选) 循环体之间的分隔符;
        open: (可选)foreach标签所循环的所有内容的开始符;
        close: (可选)foreach标签所循环的所有内容的结束符;
        index:(可选)表示当前遍历的索引。在 <foreach> 标签体中,你可以使用 #{index} 来引用当前遍历到的索引;
            遍历 list 的时候,index就是索引,item就是当前值;
            遍历 map的时候,index表示的就是map的key,item就是map的值

    -->
    <select id="getEmpByArray" resultType="Emp">
        select * from t_emp where eid in
        <foreach collection="eids" item="eid" separator="," open="(" close=")">
            #{eid}
        </foreach>
    </select>


    <!--  通过list集合实现批量添加员工
        Integer insertMoreEmpByList(List<Emp> empList);
    -->
    <insert id="insertMoreEmpByList">
        insert into t_emp values
        <foreach collection="empList" item="emp" separator=",">
            (null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null)
        </foreach>
    </insert>


    <!--  通过list集合实现批量删除员工
    Integer deleteMoreEmpByList(@Param("eids")  Integer[] eids);
    -->
    <delete id="deleteMoreEmpByList">
        delete from t_emp where eid in
        <foreach collection="eids" item="eid" separator="," open="(" close=")">
            #{eid}
        </foreach>
    </delete>
</mapper>
  • 测试类方法
java 复制代码
package com.ningxia.mybatis.test;

import com.ningxia.mybatis.mapper.DynamicMapper;
import com.ningxia.mybatis.pojo.Emp;
import com.ningxia.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

public class DynamicMapperTest {

    /**
     * 通过list集合实现批量删除员工
     */
    @Test
    public void testDeleteMoreEmpByList() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicMapper dynamicMapper = sqlSession.getMapper(DynamicMapper.class);
        Integer result = dynamicMapper.deleteMoreEmpByList(new Integer[]{28, 29, 30, 31, 32, 33, 34});
        System.out.println("成功删除员工:" + result + " 条");
    }

    /**
     * 通过list集合实现批量添加员工
     */
    @Test
    public void testInsertMoreEmpByList() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicMapper dynamicMapper = sqlSession.getMapper(DynamicMapper.class);
        Emp emp0 = new Emp(null, "小明0", 23, '男', "231568@qq.com", null, null);
        Emp emp1 = new Emp(null, "小明1", 23, '男', "231568@qq.com", null, null);
        Emp emp2 = new Emp(null, "小明2", 23, '男', "231568@qq.com", null, null);
        Emp emp3 = new Emp(null, "小明3", 23, '男', "231568@qq.com", null, null);
        Emp emp4 = new Emp(null, "小明4", 23, '男', "231568@qq.com", null, null);
        Emp emp5 = new Emp(null, "小明5", 23, '男', "231568@qq.com", null, null);
        Emp emp6 = new Emp(null, "小明6", 23, '男', "231568@qq.com", null, null);
        List<Emp> empList = Arrays.asList(emp0, emp1, emp2, emp3, emp4, emp5, emp6);
        Integer result = dynamicMapper.insertMoreEmpByList(empList);
        System.out.println("成功新增员工:" + result + " 条");
    }


    /**
     * 查询: 传递参数为一个数组
     */
    @Test
    public void testGetEmpByArray() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicMapper dynamicMapper = sqlSession.getMapper(DynamicMapper.class);
        List<Emp> empList = dynamicMapper.getEmpByArray(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
        empList.forEach(System.out::println);
    }


    /**
     * 多条件查询
     */
    @Test
    public void testGetEmpByCondition() {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicMapper dynamicMapper = sqlSession.getMapper(DynamicMapper.class);
        List<Emp> empList = dynamicMapper.getEmpByCondition(new Emp(null, "Bob", 66, '男', "bob@example.com", null, null));
//        List<Emp> empList = dynamicMapper.getEmpByCondition(new Emp(null, "Bob",null , null, "", null, null));
//        List<Emp> empList = dynamicMapper.getEmpByCondition(new Emp(null, "Kevin", null, '男', "kevin@example.com", null, null));
//        List<Emp> empList = dynamicMapper.getEmpByCondition(new Emp(null, "", null, null, "", null, null));
        empList.forEach(System.out::println);
    }

}
  • 测试结果


sql

  • 抽取重复可用的SQL片段,可以与<where>、<trim>等动态SQL标签组合使用,实现更复杂的逻辑,必须在mapper文件中批量声明,不能在其他 SQL 标签中调用(如<select>
  • mapper文件中 定义 SQL 片段,引用 SQL 片段
sql 复制代码
<!-- 定义 SQL 片段 
	id:为 SQL 片段指定唯一标识符。
-->
<sql id="userColumns">
    id, name, age, email
</sql>



<!-- 引用 SQL 片段
	refid:指定要引用的SQL片段的ID。
 -->
<select id="findUsers" resultType="User">
    SELECT
    <include refid="userColumns" />
    FROM users
</select>
  • 生成的 SQL 片段
sql 复制代码
SELECT id, name, age, email FROM users;

set

  • <set>MyBatis动态SQL的标签,集中于动态生成UPDATE语句的SET部分,简化了字段更新的逻辑,避免手动拼接内容时产生多余的重要问题。
  • 示例代码

动态更新字段

sql 复制代码
<update id="updateUser" parameterType="map">
    UPDATE users
    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="email != null">
            email = #{email},
        </if>
    </set>
    WHERE id = #{id}
</update>
  • 传入参数
json 复制代码
{
  "id": 1,
  "name": "John",
  "email": "john@example.com"
}
  • 生成 SQL
sql 复制代码
 <!--<set>自动去掉了email后多余的逗号-->

UPDATE users
SET name = 'John',
    email = 'john@example.com'
WHERE id = 1

条件更新 :如果只更新部分字段,可以根据null判断条件动态生成需要的内容。

sql 复制代码
<update id="updateUserSelective" parameterType="User">
    UPDATE users
    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="email != null">
            email = #{email},
        </if>
    </set>
    WHERE id = #{id}
</update>
  • 传入参数
json 复制代码
{
  "id": 2,
  "age": 25
}
  • 生成 SQL
sql 复制代码
UPDATE users SET age = 25 WHERE id = 2

配合动态字段的更新

sql 复制代码
<update id="updateDynamic">
    UPDATE users
    <set>
        <foreach collection="fields" index="key" item="value" separator=",">
            ${key} = #{value}
        </foreach>
    </set>
    WHERE id = #{id}
</update>
  • 传入参数
json 复制代码
{
  "id": 3,
  "fields": {
    "name": "Alice",
    "email": "alice@example.com"
  }
}
  • 生成 SQL
sql 复制代码
UPDATE users
SET name = 'Alice',
    email = 'alice@example.com'
WHERE id = 3
相关推荐
u01005596014 小时前
MySQL 中的左连接查询(LEFT JOIN)是一种 SQL 连接操作
java·数据库·mybatis
武子康18 小时前
Java-16 深入浅出 MyBatis - SqlSession Executor StatementHandler 源码分析
java·开发语言·mysql·mybatis·springboot
你疯了抱抱我19 小时前
【自用】管材流转项目后端重部署流程 SpringBoot MyBatis PLUS
spring boot·后端·mybatis
zxguan1 天前
Mybatis 学习 之 XML 手册
xml·学习·mybatis
云初@1 天前
MyBatis动态SQL处理增删改查
数据库·sql·mybatis
深情废杨杨1 天前
后端-mybatis的一对多
java·spring boot·mybatis
爱上语文2 天前
数据库多表设计
java·开发语言·数据库·后端·mybatis
计算机学姐2 天前
基于SpringBoot+Vue的美妆购物网站
java·vue.js·spring boot·后端·mysql·spring·mybatis
墨鸦_Cormorant2 天前
MyBatis-Plus介绍及基本使用
mybatis·mybatisplus·mybatis-plus
计算机学姐2 天前
基于SpringBoot的汽车销售网站
java·spring boot·后端·mysql·汽车·intellij-idea·mybatis