Javaweb学习记录 --- MyBatis
日期: 11.8
📖今日知识点 ------Mybatis简介
-
什么是Mybatis?
1.Mybatis是一款优秀的持久层框架,用于简化JDBC开发 2.Mybatis本是Apache的一个开源项目iBatis,2021年这个项目有apache software foundation迁移到了Google code,并且改名为Mybatis。2013年11月迁移到GitHub。 3.[官网](https://mybatis.org/mybatis-3/zh/index.html) -
什么是持久层?
1.负责将数据保存到数据库的那一层代码
2.JavaEE三层架构:表现层、业务层、持久层
-
什么是框架?
1.框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型 2.在框架的基础上构建软件编写更加高效、规范、通用、可扩展。
📖今日知识点 ------JDBC 与 MyBatis 对比
传统 JDBC 开发存在两大痛点,MyBatis 分别给出了对应的改进方案:
| 传统 JDBC 问题 | 具体表现 | MyBatis 改进方案 |
|---|---|---|
| 硬编码 | 注册驱动、获取连接写死在代码里 | 将数据库连接信息提取到 mybatis-config.xml 中 |
| 硬编码 | SQL 语句散落在 Java 代码中 | 将 SQL 语句提取到 Mapper 映射文件中 |
| 操作繁琐 | 手动设置参数 | 框架自动解析并设置参数 |
| 操作繁琐 | 手动封装结果集 | 框架自动将结果集映射为 Java 对象 |
简单来说:硬编码 → 配置文件,操作繁琐 → 自动完成。
💻练习代码
传统JDBC代码:
java
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 获取Connection连接
String url = "jdbc:mysql:///db1?useSSL=false";
String uname = "root";
String pwd = "1234";
Connection conn = DriverManager.getConnection(url, uname, pwd);
// 3. 定义sql
String gender = "男";
String sql = "select * from tb_user where gender = ?";
// 4. 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
// 5. 设置?的值
pstmt.setString(1, gender);
// 6. 执行sql
ResultSet rs = pstmt.executeQuery();
// 7. 封装结果集(手动映射)
User user = null;
ArrayList<User> users = new ArrayList<>();
while (rs.next()) {
// 获取数据
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
// 创建对象,设置属性值
user = new User();
user.setId(id);
user.setUsername(username);
user.setPassword(password);
user.setGender(gender);
// 装入集合
users.add(user);
}
MyBatis 简化后的实现
- 配置文件(替代硬编码的连接信息)
java
<!-- 连接池信息 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///db1?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
- 映射文件(替代硬编码的SQL与手动封装)
java
<select id="selectByGender" parameterType="string" resultType="com.itheima.pojo.User">
select * from tb_user where gender = #{gender};
</select>
- Java 调用代码(极其精简)
java
List<User> users = sqlSession.selectList("test.selectByGender", "男");
📖今日知识点 ------MyBatis快速入门
目标:查询 user 表中所有数据
操作步骤:
1. 创建 user 表,添加数据
sql
create database mybatis;
use mybatis;
drop table if exists tb_user;
create table tb_user(
id int primary key auto_increment,
username varchar(20),
password varchar(20),
gender char(1),
addr varchar(30)
);
insert into tb_user values (1,'zhangsan','123','男','北京');
insert into tb_user values (2,'李四','234','女','天津');
insert into tb_user values (3,'王五','11','男','西安');
2. 创建模块,导入坐标
pom.xml:
java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mybatis-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- MySQL 驱动:升级至 8.2.0,修复 CVE-2023-22102 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>
<!-- 显式引入 protobuf-java 高版本,修复传递依赖 CVE-2024-7254 -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.5</version>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- SLF4J API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<!-- Logback:升级至 1.3.15,修复 CVE-2024-12798 等 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.15</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.3.15</version>
</dependency>
</dependencies>
</project>
3. 编写 MyBatis 核心配置文件
作用:替换连接信息,解决硬编码问题
mybatis -config.xml
java
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 数据库连接池 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 加载sql映射文件 -->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
4. 编写 SQL 映射文件
作用:统一管理 SQL 语句,解决硬编码问题
UserMapper.xml:
java
<?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: 名称空间
select id:sql语句的唯一标识
resultType:结果集的类型,对应pojo类名
-->
<mapper namespace="test">
<select id="selectAll" resultType="com.itheima.pojo.User">
select * from tb_user;
</select>
</mapper>
5. 编码
1.定义 POJO 类
2.加载核心配置文件,获取 SqlSessionFactory 对象
3获取 SqlSession 对象,执行 SQL 语句
4.释放资源
定义POJO类(User类)
java
package com.itheima.pojo;
//alt + 鼠标左键 :整列编辑
public class User {
//属性:
private Integer id;
private String username;
private String password;
private String gender;
private String adder;
//get/set方法:
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAdder() {
return adder;
}
public void setAdder(String adder) {
this.adder = adder;
}
//toString方法:
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", adder='" + adder + '\'' +
'}';
}
}
java
package com.itheima.pojo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisDemo {
public static void main(String[] args) throws IOException {
//1.加载mybatis的核心配置文件,获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 执行sql语句
//参数:namespace.id
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);
//4. 关闭SqlSession对象
sqlSession.close();
}
}
打印结果:

📖今日知识点 ------解决SQL语句警告提示
-
SQL语句警告:

-
产生原因:Idea和数据库没有建立连接,不识别表的信息
-
解决方式:在Idea配置MySQL数据库连接
1.在右上角找到Database,点击加号,Data Source,MySQL。

2.填写如下:

3.可以在idea直接新建查询窗口

📖今日知识点 ------Mapper代理开发
-
定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下。
UserMapper接口和UserMapper.xml在同一目录下:

-
设置SQL映射文件的namespace属性为Mapper接口全限定名。
将namespace改为UserMapper接口的全限定名:

-
在 Mapper 接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
java
package com.itheima.mapper;
import com.itheima.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> selectAll();
}
-
编码
1.通过 SqlSession 的 getMapper方法获取 Mapper接口的代理对象 2.调用对应方法完成sql的执行
java
package com.itheima.pojo;
import com.itheima.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisDemo2 {
public static void main(String[] args) throws IOException {
//1.加载mybatis的核心配置文件,获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 执行sql语句
//参数:namespace.id
//List<User> users = sqlSession.selectList("test.selectAll");
//3.1获取UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
System.out.println(users);
//4. 关闭SqlSession对象
sqlSession.close();
}
}
- 细节:如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载:

运行结果:

📖今日知识点 ------Mybatis核心配置文件
MyBatis 核心配置文件(mybatis-config.xml)的顶层结构如下,<configuration> 是根节点,其余标签都是它的子节点:
| 配置标签 | 说明 |
|---|---|
configuration |
配置文件的根节点,所有配置都写在其中 |
properties |
属性,用于外部化配置(如数据库连接参数),可被其他配置引用 |
settings |
设置,MyBatis 的全局行为开关(如驼峰映射、日志实现等) |
typeAliases |
类型别名,为 Java 类型设置简短别名,简化映射文件中的书写 |
typeHandlers |
类型处理器,负责 Java 类型与 JDBC 类型之间的转换 |
objectFactory |
对象工厂,用于创建结果集映射后的对象实例 |
plugins |
插件,通过拦截器机制扩展 MyBatis 功能(如分页插件) |
environments |
环境配置,可配置多套环境(开发、测试、生产) |
environment |
环境变量,environments 的子节点,代表一套具体环境 |
transactionManager |
事务管理器,配置事务管理方式(如 JDBC、MANAGED) |
dataSource |
数据源,配置数据库连接信息(如 POOLED、UNPOOLED、JNDI) |
databaseIdProvider |
数据库厂商标识,支持多数据库厂商的 SQL 适配 |
mappers |
映射器,注册 SQL 映射文件或 Mapper 接口 |
注意:这些标签在配置文件中有严格的顺序要求,必须按照上表顺序书写,否则会报错。
📖今日知识点 ------Mybatis案例 --- 环境准备
-
数据库表 tb_brand

-
实体类 Brand
java
package com.itheima.pojo;
public class Brand {
// id 主键
private Integer id;
// 品牌名称
private String brandName;
// 企业名称
private String companyName;
// 排序字段
private Integer ordered;
// 描述信息
private String description;
// 状态:0: 禁用 1: 启用
private Integer status;
//get/set方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Integer getOrdered() {
return ordered;
}
public void setOrdered(Integer ordered) {
this.ordered = ordered;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
//toString方法
@Override
public String toString() {
return "Brand{" +
"id=" + id +
", brandName='" + brandName + '\'' +
", companyName='" + companyName + '\'' +
", ordered=" + ordered +
", description='" + description + '\'' +
", status=" + status +
'}';
}
}
-
测试用例
-
安装 MyBatisX 插件
1.MyBatisX 是一款基于 IDEA 的快速开发插件,为效率而生。 2.主要功能: XML 和 接口方法 相互跳转 根据接口方法生成 statement 3.安装:

打开UserMapper.xml和UserMapper.Java会有带着红色和蓝色头绳的小鸟

📖今日知识点 ------查询---查询所有结果&结果映射
-
编写接口方法:Mapper接口
参数:无 结果:List<Brand>
java
package com.itheima.mapper;
import com.itheima.pojo.Brand;
import java.util.List;
public interface BrandMapper {
/**
* 查询所有品牌
*/
public List<Brand> selectAll();
}
- 编写SQL语句:SQL映射文件:
java
<?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: 名称空间
select id:sql语句的唯一标识
resultType:结果集的类型,对应pojo类名
-->
<mapper namespace="com.itheima.mapper.BrandMapper">
<!--
alt+enter 自动生成sql语句的唯一标识
-->
<select id="selectAll" resultType="com.itheima.pojo.Brand">
select * from tb_brand;
</select>
</mapper>
- 执行方法,测试
java
package com.itheima.test;
import com.itheima.mapper.BrandMapper;
import com.itheima.pojo.Brand;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyBatisTest {
@Test
public void testSelectAll() throws IOException {
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);
//5.释放资源
sqlSession.close();
}
}
总而言之:Mybatis完成操作需要几步?三步:编写接口方法-->编写SQL->执行方法
💻练习代码
运行结果:
(但是有些没有封装,比如brandName,companyName等,因为pojo包里的Brand里命名属性时采用驼峰命名法,而数据库字段名为brand_name,company_name,不一样对应不上,所以不能自动封装。)

⬇️有3种手动封装的方法:
java
<?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: 名称空间
select id:sql语句的唯一标识
resultType:结果集的类型,对应pojo类名
-->
<mapper namespace="com.itheima.mapper.BrandMapper">
<!--
alt+enter 自动生成sql语句的唯一标识
-->
<!--
数据库的字段名称 和 实体类的属性名称不一样时,不能自动封装数据
解决方法:
1.起别名:对数据库的字段起别名,与实体类的属性名称一致(缺点:每次查询都要定义一次别名
sql片段:缺点:不灵活
2.使用resultMap:
1.定义<resultMap>标签,用于映射数据库的字段和实体类的属性
2.在<select>标签中使用resultMap属性,指定使用哪个resultMap
-->
<!-- 起别名
<select id="selectAll" resultType="com.itheima.pojo.Brand">
# select id,brand_name as brandName,company_name as companyName,ordered,description,status
# from tb_brand;
</select>
-->
<!-- sql片段 -->
<!--
id:完成主键字段的映射
column:表的列名
property:实体类的属性名
result:完成其他字段的映射
column:表的列名
property:实体类的属性名
-->
<!--<sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>
<select id="selectAll" resultType="brand">
select
<include refid="brand_column" />
from tb_brand;
</select>
-->
<!-- 使用resultMap -->
<resultMap id="brandResultMap" type="com.itheima.pojo.Brand">
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select * from tb_brand;
</select>
</mapper>
📖今日知识点 ------查询---查看详情
-
编写接口方法:Mapper接口
参数:id 结果:Brand
java
/**
* 查看详情:根据id查询品牌详情
*/
Brand selectById(int id);
- 编写 SQL语句:SQL映射文件
java
<!--
* 参数占位符:
1. #{}:会将其替换为 ?,为了防止SQL注入
2. ${}:拼sql。会存在SQL注入问题
3. 使用时机:
* 参数传递的时候:#{}*
* 表名或者列名不固定的情况下:${} 会存在SQL注入问题
* 参数类型:parameterType:可以省略
* 特殊字符处理:
1. 转义字符:
2. CDATA区
-->
<!-- 查看详情:根据id查询品牌详情 -->
<select id="selectById" resultMap="brandResultMap">
select *
from tb_brand where id
<![CDATA[
<
]]>
#{id};
</select>
- 执行方法,测试
java
//测试根据id查询品牌详情
@Test
public void testSelectById() throws IOException {
//接收参数
int id = 1;
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
Brand brand = brandMapper.selectById(id);
System.out.println(brand);
//5.释放资源
sqlSession.close();
}
总结:
-
参数占位符:
1.#{}:执行SQL时,会将#{}占位符替换为?,将来自动设置参数值 2.${}:拼SQL。会存在SQL注入问题 3.使用时机: 参数传递,都使用#{} 如果要对表名、列名进行动态设置,只能使用${}进行sql拼接。 -
parameterType:
用于设置参数类型,该参数可以省略 -
SQL语句中特殊字符处理:
转义字符 <![CDATA[内容]]>
📖今日知识点 ------查询 --- 条件查询
-
编写接口方法:Mapper接口
参数:所有查询条件 结果:List<Brand>
java
/**
* 条件查询
* 参数接收:
* SQL 参数占位符名称就是你在 XML 映射文件里写在 #{} 里面的那个名字。
* 1.散装参数:如果方法中有多个参数,需要使用@param("SQL参数占位符名称")
* 2,对象参数:对象属性名称要和参数占位符名称一致,否则会报错
* 3,Map集合参数
*
*
*
*
*/
//List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName);
/**
* 多条件查询 - 实体类封装参数
*/
//List<Brand> selectByCondition(Brand brand);
/**
* 多条件查询 - Map集合封装参数
*/
List<Brand> selectByCondition(Map map);
- 编写SQL语句:SQL映射文件
java
<!-- 条件查询 -->
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
- 执行方法,测试
java
//测试条件查询
@Test
public void testSelectByCondition() throws IOException {
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
//处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//封装成对象
// Brand brand = new Brand();
// brand.setStatus(status);
// brand.setCompanyName(companyName);
// brand.setBrandName(brandName);
Map map = new HashMap();
map.put("status",status);
map.put("companyName",companyName);
map.put("brandName",brandName);
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
// List<Brand> brands = brandMapper.selectByCondition(status,companyName,brandName);
// List<Brand> brands = brandMapper.selectByCondition(brand);
List<Brand> brands = brandMapper.selectByCondition(map);
System.out.println(brands);
//5.释放资源
sqlSession.close();
}
-
总结:
SQL语句设置多个参数有几种方式?
1.散装参数:需要使用@Param("SQL中的参数占位符名称") 2.实体类封装参数 只需要保证SQL中的参数名 和 实体类属性名对应上,即可设置成功 3.map集合 只需要保证SQL中的参数名 和 map集合的键的名称对应上,即可设置成功
📖今日知识点 ------查询 --- 动态条件查询
一、查询-多条件-动态条件查询
-
SQL语句会随着用户的输入或外部条件的变化而变化,我们称为 动态SQL
-
MyBatis 对动态SQL有很强大的支撑:
if:用于判断参数是否有值,使用test属性进行条件判断 choose (when, otherwise) trim (where, set) foreach
java
<!--
动态条件查询
* if:条件判断
* test:逻辑表达式
* 问题:
* 恒等式
* <where> 替换 where 关键字
-->
<!-- 动态条件查询 -->
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
/* where 1 = 1*/
<where>
<if test="status != null">
status = #{status}
</if>
<if test="companyName != null and companyName != ''">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != ''">
and brand_name like #{brandName}
</if>
</where>
</select>
-
存在的问题:第一个条件不需要逻辑运算符
解决方案: 1.使用恒等式让所有条件格式都一样 2.<where>标签替换 where 关键字
java
//测试条件查询
@Test
public void testSelectByCondition() throws IOException {
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
//处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//封装成对象
Brand brand = new Brand();
//brand.setStatus(status);
//brand.setBrandName(brandName);
brand.setCompanyName(companyName);/*
Map map = new HashMap();
map.put("status",status);
map.put("companyName",companyName);
map.put("brandName",brandName);
*/
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
List<Brand> brands = brandMapper.selectByCondition(brand);
// List<Brand> brands = brandMapper.selectByCondition(brand);
// List<Brand> brands = brandMapper.selectByCondition(map);
System.out.println(brands);
//5.释放资源
sqlSession.close();
}

二、查询-单条件-动态条件查询
-
从多个条件中选择一个
choose (when, otherwise):选择,类似于Java中的 switch 语句

💻练习代码
1.Mapper接口:
java
//单条件,动态查询
List<Brand> selectByConditionSingle(Brand brand);
2.编写SQL语句:SQL映射文件:
java
<!--动态单条件查询-->
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
where
<choose><!-- 相当于switch-->
<when test="status != null">
status = #{status}
</when> <!-- 相当于case-->
<when test="companyName != null and companyName != ''">
and company_name like #{companyName}
</when> <!-- 相当于case-->
<when test="brandName != null and brandName != ''">
and brand_name like #{brandName}
</when> <!-- 相当于case-->
<otherwise>
1 = 1
</otherwise>
</choose>
</select>
3.执行方法,测试:
java
//测试条件查询,单条件动态
@Test
public void testSelectByConditionSingle() throws IOException {
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
//处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//封装成对象
Brand brand = new Brand();
brand.setStatus(status);
//brand.setBrandName(brandName);
//brand.setCompanyName(companyName);
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
// List<Brand> brands = brandMapper.selectByCondition(brand);
// List<Brand> brands = brandMapper.selectByCondition(brand);
// List<Brand> brands = brandMapper.selectByCondition(map);
List<Brand> brands = brandMapper.selectByConditionSingle(brand);
System.out.println(brands);
//5.释放资源
sqlSession.close();
}
📖今日知识点 ------添加
-
编写接口方法:Mapper接口
代码框:void add(Brand brand); 参数:除了id之外的所有数据 结果:void
java
/**
* 添加
*/
void add(Brand brand);
- 编写 SQL语句:SQL映射文件
java
<!-- 添加 -->
<insert id="add">
insert into tb_brand(brand_name,company_name,ordered,description,status)
values(#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>
-
执行方法,测试
MyBatis事务: openSession(): 默认开启事务,进行增删改操作后需要使用 sqlSession.commit(); 手动提交事务 openSession(true): 可以设置为自动提交事务(关闭事务)
java
//添加
@Test
public void testAdd() throws IOException {
//接收参数
int status = 1;
String companyName = "波导手机";
String brandName = "b波导";
String description = "波导手机,手机中的战斗机";
int ordered = 100;
//封装成对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setBrandName(brandName);
brand.setCompanyName(companyName);
brand.setDescription(description);
brand.setOrdered(ordered);
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
brandMapper.add(brand);
//提交.提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
-
在数据添加成功后,需要获取插入数据库数据的主键的值,加useGeneratedKeys="true" keyProperty="id即可
比如:添加订单和订单项 1.添加订单 2.添加订单项,订单项中需要设置所属订单的id
java
<!-- 添加
返回添加数据的主键
useGeneratedKeys:开启自动获取主键值
keyProperty:指定主键值的属性名
-->
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tb_brand(brand_name,company_name,ordered,description,status)
values(#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>
📖今日知识点 ------修改功能
一、修改全部字段
-
编写接口方法:Mapper接口
参数:所有数据 结果:int(返回影响行数)
java
/**
* 修改--全部字段
*/
int update(Brand brand);
- 编写 SQL语句:SQL映射文件
java
<!-- 修改全部字段
-->
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id = #{id};
</update>
- 执行方法,测试
java
//修改全部字段
@Test
public void testUpdate() throws IOException {
//接收参数
int status = 1;
String companyName = "苹果手机";
String brandName = "苹果";
String description = "苹果手机,苹果公司生产的一款智能设备";
int ordered = 5000;
int id = 5;
//封装成对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setBrandName(brandName);
brand.setCompanyName(companyName);
brand.setDescription(description);
brand.setOrdered(ordered);
brand.setId(id);
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
int count = brandMapper.update(brand);
System.out.println(count);
//提交.提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
- 修改结果:

二、修改动态字段
-
编写接口方法:Mapper接口
参数:部分数据,封装到对象中 结果:int代码和修改全部字段的一样:
java
int update(Brand brand);
- 编写 SQL语句:SQL映射文件
加了if语句判断,和set标签
java
<!-- 修改部分字段
-->
<update id="update">
update tb_brand
<set>
<if test="brandName != null and brandName != ''">
brand_name = #{brandName},
</if>
<if test="companyName != null and companyName != ''">
company_name = #{companyName},
</if>
<if test="ordered != null">
ordered = #{ordered},
</if>
<if test="description != null and description != ''">
description = #{description},
</if>
<if test="status != null">
status = #{status}
</if>
</set>
where id = #{id};
**为什么这里要加 `<set>`?**
如果不加 `<set>`,直接写 `update tb_brand set ...`,会带来两个问题:
1. **逗号残留导致 SQL 语法错误**:`<if>` 是动态拼接的,如果最后一个生效的条件后面带着逗号(比如 `status = #{status},`),拼出来的 SQL 就会变成 `... status = ? , where id = ?`,多出一个逗号,直接报错。
2. **所有条件都不满足时 SQL 不合法**:如果传入的对象所有字段都为 null,`set` 后面就空了,变成 `update tb_brand where id = ?`,语法错误。
`<set>` 标签的作用就是**自动处理这两件事**:
- 它会自动在拼接内容前加上 `SET` 关键字;
- 它会**自动去掉最后一个多余的逗号**;
- 如果所有 `<if>` 都不成立,它不会生成 `SET` 关键字(此时 SQL 不合法,所以业务上至少要保证有一个字段被更新)。
所以上面代码里每个 `<if>` 内部都放心地写了逗号,交给 `<set>` 去裁剪,最终生成的 SQL 类似:
```sql
update tb_brand
set brand_name = ?, company_name = ?, ordered = ?
where id = ?;
补充:
<set>本质是<trim prefix="SET" suffixOverrides=",">的简写,和<where>标签是同一套思路------<where>负责去掉多余的AND/OR,<set>负责去掉多余的逗号。
```
- 执行方法,测试
注:记得把brand.setId(id);里的id改成具体值
java
//修改部分字段
@Test
public void testUpdate() throws IOException {
//接收参数
int status = 1;
String companyName = "苹果";
String brandName = "ipad";
String description = "ipad,苹果公司生产的一款智能平板";
int ordered = 5000;
int id = 5;
//封装成对象
Brand brand = new Brand();
//brand.setStatus(status);
brand.setBrandName(brandName);
//brand.setCompanyName(companyName);
brand.setDescription(description);
//brand.setOrdered(ordered);
brand.setId(5);
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
int count = brandMapper.update(brand);
System.out.println(count);
//提交.提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
- 执行结果

- 刷新表就能看到修改后的结果:

📖今日知识点 ------删除功能
一、删除单个
-
编写接口方法:Mapper接口
参数:id 结果:void
java
/**
* 根据id删除--单个
*/
void deleteById(int id);
- 编写 SQL语句:SQL映射文件
java
<!-- 根据id删除-单个 -->
<delete id="deleteById">
delete from tb_brand where id = #{id};
</delete>
- 执行方法,测试
java
//根据id删除--单个
@Test
public void testDeleteById() throws IOException {
//接收参数
int id = 6;
//封装成对象
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
brandMapper.deleteById(id);
//提交.提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
- 删除结果:

二、批量删除
-
编写接口方法:Mapper接口
参数:id数组 结果:void
java
/**
* 根据id删除--批量
*/
void deleteByIds(@Param("ids") int[] ids);
- 编写 SQL语句:SQL映射文件
java
<!-- 根据id删除-批量 -->
<!--
Mybatis会将数组参数,封装成Map对象
默认:array = 数组
使用@Param注解改变map集合的默认key名称
-->
<delete id="deleteByIds">
delete from tb_brand where id
in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
;
</delete>
- 执行方法,测试
java
//根据id删除--批量
@Test
public void testDeleteByIds() throws IOException {
//接收参数
int[] ids = {5,3,2};
//封装成对象
//1.获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
brandMapper.deleteByIds(ids);
//提交.提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
-
批量删除成功:

-
刷新表格批量删除成功:

-
💡 核心知识点解析: 标签
这个标签非常强大,专门用来遍历集合或数组。
collection="ids":指定要遍历的集合名称。对应接口里 @Param("ids") 起的名字。 item="id":遍历出来的每一个元素,临时命名为 id。下面的 #{id} 就是取这个值。 separator=",":每次循环拼接完后,用逗号隔开。 open="(":拼接的 SQL 语句以左括号开始。 close=")":拼接的 SQL 语句以右括号结束。
📖今日知识点 ------参数传递
一、概述:
MyBatis 接口方法中可以接收各种各样的参数,MyBatis 底层对于这些参数进行不同的封装处理方式。MyBatis 提供了 ParamNameResolver 类来进行参数封装。
二、 单个参数
当接口方法只有一个参数时,MyBatis 的处理方式如下:
-
POJO 类型:直接使用,要求实体类属性名和参数占位符名称一致。
-
Map 集合:直接使用,要求键名和参数占位符名称一致。
-
Collection 集合:封装为 Map 集合。
map.put("collection", collection集合); map.put("arg0", collection集合); -
List 集合:封装为 Map 集合。
map.put("collection", list集合); map.put("list", list集合); map.put("arg0", list集合); -
Array 数组:封装为 Map 集合。
map.put("array", 数组); map.put("arg0", 数组); -
其他类型:直接使用。
三、 多个参数
当接口方法有多个参数时,MyBatis 会将它们封装为一个 Map 集合:
map.put("arg0", 参数值1);
map.put("param1", 参数值1);
map.put("arg1", 参数值2);
map.put("param2", 参数值2);
四、推荐最佳实践:使用 @Param 注解
💡 核心建议: 将来都使用 @Param 注解来修改 Map 集合中默认的键名,并使用修改后的名称来获取值,这样可读性更高!
示例:
java
User select(@Param("username") String username, @Param("password") String password);
对应的 XML 映射:
java
<select id="select" resultType="user">
select *
from tb_user
where
username = #{username}
and password = #{password};
</select>
使用 @Param 后,底层 Map 的封装变为:
map.put("username", 参数值1);
map.put("param1", 参数值1);
map.put("arg1", 参数值2);
五、 思考题 / 场景分析
代码示例:
java
String username = "zhangsan";
String password = "123";
Map map = new HashMap();
User user = new User();
user.setUsername(username);
user.setPassword(password);
map.put("user", user);
User user = userMapper.select(map);
对应的 XML 查询:
java
<select id="select" resultType="user">
select *
from tb_user
where
username = #{?}
and password = #{?}
</select>
思考:当传入的是一个 Map 集合,且 Map 中包含一个 User 对象时,XML 中的占位符该如何正确填写?(答案:#{user.username} 和 #{user.password})
📖今日知识点 ------注解开发---注解完成增删改查
核心观点:使用注解开发会比配置文件开发更加方便。
一、 基本用法
直接在 Mapper 接口方法上使用注解编写 SQL 语句。
代码示例:
java
@Select("select * from tb_user where id = #{id}")
public User selectById(int id);
二、 常用 CRUD 注解
MyBatis 提供了以下四个基础注解对应数据库的增删改查操作:
查询:@Select
添加:@Insert
修改:@Update
删除:@Delete
三、 使用建议与提示
💡 提示:
注解完成简单功能
配置文件(XML)完成复杂功能
四、官方说明解读:
使用注解来映射简单语句会使代码显得更加简洁。
但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
灵活选择:选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。换句话说,永远不要拘泥于一种方式,你可以很轻松地在基于注解和 XML 的语句映射方式间自由移植和切换。