MyBatis快速入门(中)
- 四、全局配置文件configuration标签中子标签顺序
- 1、子标签顺序
- 2、子标签说明
- 3、`<properties>` 标签和 `<environments>` 标签详述
- 4、配置文件代码示例
- 五、MyBatis 基础功能之 resultMap
- 1、建表语句
- 2、解决表中字段名和类中属性名不一致
- (1)配置 驼峰命名转换
- (2)使用别名 AS 配合字段映射
- (3)使用 @Results 和 @Result 注解
- (4)使用 Map 类型
- (5)使用 `<resultMap>` 配置全局映射
- 3、resultMap 映射
- (1)字段映射
- (2)多对一关联映射
- (a)使用 `AS` 别名 (as 可省略)
- (b)resultMap 级联属性赋值
- (c)使用 association 标签定义关联的单个对象的封装
- (d)association 分段查询
- (😀)分步查询之延迟加载(按需加载)
- (😀)lazyLoadingEnabled 与 aggressiveLazyLoading
- (3)一对多关联映射
- (a)使用 collocation 处理一对多的映射关系
- (b)使用 collocation 分步查询
- 五、MyBatis 基础功能之动态 SQL
- if
- where
- trim
- choose...when...otherwise
- foreach
- sql
- set
官方文档
四、全局配置文件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
文件,不同环境的数据库连接信息分别如下:
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 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 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、建表语句
-- 创建 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
标签在全局配置文件中设置,即配置驼峰命名转换
。
<configuration>
<settings>
<!-- 开启驼峰命名转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
(2)使用别名 AS 配合字段映射
也可以在
SQL
查询中使用别名,使字段名
与属性名
相匹配:
<!--查询所有员工信息-->
<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
映射文件了
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
映射文件了
public interface EmpMapper {
/**
* 查询所有员工信息
*/
@Select("SELECT * FROM t_emp")
List<Map<String, Object>> getAllEmp();
}
注意测试方法接收数据类型
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
:例如:
<?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)
的关系是多对一
的; 多个员工指向一个部门,因此这是典型的多对一
关系。
- 实体类
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
接口方法
/**
* 根据员工id查询员工信息和员工所在部门信息
* <p>
* 分步查询 根据员工id查询员工信息和员工所在部门信息
* 1、 先根据员工id查询员工信息
* 2、 再根据员工信息中的部门id去获取对应的部门信息
*/
Map<String, Object> getEmpAndDeptByIdOne(@Param("id") Integer id);
EmpMapper
映射文件
<?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>
- 测试类方法
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
接口方法
/**
* 根据员工id查询员工信息和员工所在部门信息
* <p>
* 分步查询 根据员工id查询员工信息和员工所在部门信息
* 1、 先根据员工id查询员工信息
* 2、 再根据员工信息中的部门id去获取对应的部门信息
*/
Emp getEmpAndDeptByIdTwo(@Param("id") Integer id);
EmpMapper
映射文件
<?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>
- 测试类方法
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
接口方法
/**
* 根据员工id查询员工信息和员工所在部门信息
* <p>
* 分步查询 根据员工id查询员工信息和员工所在部门信息
* 1、 先根据员工id查询员工信息
* 2、 再根据员工信息中的部门id去获取对应的部门信息
*/
Emp getEmpAndDeptByIdThree(@Param("id") Integer id);
EmpMapper
映射文件
<?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>
- 测试类方法
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
接口方法
/**
* 根据员工id查询员工信息和员工所在部门信息
* <p>
* 分步查询 根据员工id查询员工信息和员工所在部门信息
* 1、 先根据员工id查询员工信息
* 2、 再根据员工信息中的部门id去获取对应的部门信息
*/
Emp getEmpAndDeptByIdFour(@Param("id") Integer id);
EmpMapper
映射文件
<?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>
- 测试类方法
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
中写的,也可以分开写,Emp
和Dept
的信息各自写在自己的映射文件中
EmpMapper
接口方法
/**
* 根据员工id查询员工信息和员工所在部门信息
* <p>
* 分步查询 根据员工id查询员工信息和员工所在部门信息
* 1、 先根据员工id查询员工信息
* 2、 再根据员工信息中的部门id去获取对应的部门信息
*/
Emp getEmpAndDeptByIdFive(@Param("id") Integer id);
EmpMapper
映射文件
<?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 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>
- 测试类方法
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
配置文件
<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
lazyLoadingEnabled
和aggressiveLazyLoading
是两个相辅相成但有不同作用的配置,它们的设计并不多余,而是用来满足不同的延迟加载场景需求。
<settings>
<setting name="lazyLoadingEnabled" value="true" />
<setting name="aggressiveLazyLoading" value="true" />
</settings>
访问代码:
Order order = orderMapper.selectById(1);
// 访问 customer 时
System.out.println(order.getCustomer().getName()); // 触发延迟加载,加载 customer 和 items。
- 一次性加载了
customer
和items
,即使你只访问了customer
。
(3)一对多关联映射
在
一
中创建多
所对应的集合
- (如下案例:在实体类
Dept
中创建Emp
对应的集合对象)
案例 : 如果关联的是一个
集合
时
【如:查询部门
信息的时候,把当前部门所有员工
信息也查询出来】
- 实体类
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
接口方法
/**
* 获取部门及部门中所有员工信息
*/
Dept getDeptAndEmpInfoById(@Param("id") Integer id) ;
DeptMapper
映射文件
<?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>
- 测试类方法
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
接口方法
/**
* 获取部门及部门中所有员工信息
* 分步查询
*/
Dept getDeptAndEmpInfoByStep(@Param("id") Integer id) ;
DeptMapper
映射文件
<?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>
- 测试类方法
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
中;
<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
后面直接拼接and
,SQL
语句错误导致查询失败 -
解决办法:
-
where
后面拼接1=1
这样的类似语句
- 使用
<where>
标签
where
- 当
where
标签中有内容时,会自动生成where
关键字,并且将内容前多余的and
或or
去掉;- 当
where
标签中没有内容时,此时where
标签没有任何效果;
注意:
where
标签 不能 将内容后面多余的and
或or
去掉;
<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
标签 不能 将内容后面多余的and
或or
去掉; - 解决办法:使用
<trim>
标签
trim
当标签中有内容时:
prefix
:前缀,给拼串后的整个字符串加一个前缀prefixOverrides
:前缀覆盖,去掉整个字符串前面多余的字符串suffix
:后缀,给拼串后的整个字符串加一个后缀suffixOverrides
:后缀覆盖,去掉整个字符串后面多余的字符
当标签中
没有
内容时,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>
choose…when…otherwise
choose
标签 (when,otherwise
): 分支选择,类似if...else if...else
或带了break
的swtich-case
,只会进入其中一个分支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>
foreach
collection
: 设置需要循环的数组或集合
;item
: 表示数组或集合中的每一个对象
;separator
:(可选) 循环体之间的分隔符
;open
:(可选)foreach
标签所循环的所有内容的开始符
;close
:(可选)foreach
标签所循环的所有内容的结束符
;index
:(可选) 表示当前遍历的索引。在<foreach>
标签体中,你可以使用#{index}
来引用当前遍历到的索引;
- 遍历
list
的时候,index
就是索引,item
就是当前值;- 遍历
map
的时候,index
表示的就是map
的key
,item
就是map
的值
DynamicMapper
接口方法
/**
* 查询: 传递参数为一个数组
*/
List<Emp> getEmpByArray(@Param("eids") Integer[] eids);
/**
* 通过list集合实现批量添加员工
*/
Integer insertMoreEmpByList(@Param("empList") List<Emp> empList);
/**
* 通过list集合实现批量删除员工
*/
Integer deleteMoreEmpByList(@Param("eids") Integer[] eids);
DynamicMapper
映射文件
<?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>
- 测试类方法
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 片段
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
片段
SELECT id, name, age, email FROM users;
set
<set>
是MyBatis
动态SQL
的标签,集中于动态生成UPDATE
语句的SET
部分,简化了字段更新的逻辑,避免手动拼接内容时产生多余的重要问题。
- 示例代码
动态更新字段
<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>
- 传入参数
{
"id": 1,
"name": "John",
"email": "john@example.com"
}
- 生成
SQL
:
<!--<set>自动去掉了email后多余的逗号-->
UPDATE users
SET name = 'John',
email = 'john@example.com'
WHERE id = 1
条件更新:如果只更新部分字段,可以根据
null
判断条件动态生成需要的内容。
<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>
- 传入参数
{
"id": 2,
"age": 25
}
- 生成
SQL
:
UPDATE users SET age = 25 WHERE id = 2
配合动态字段的更新:
<update id="updateDynamic">
UPDATE users
<set>
<foreach collection="fields" index="key" item="value" separator=",">
${key} = #{value}
</foreach>
</set>
WHERE id = #{id}
</update>
- 传入参数
{
"id": 3,
"fields": {
"name": "Alice",
"email": "alice@example.com"
}
}
- 生成
SQL
:
UPDATE users
SET name = 'Alice',
email = 'alice@example.com'
WHERE id = 3