2025.05.17

JavaWeb学习之Mybatis

一、Mybatis入门

1.1 Mybatis简介

  • Mybatis是一款优秀的持久层框架,用于简化JDBC的开发
  • 官网:https://mybatis.org/mybatis-3/zh/index.html
  • Mybatis的特点:
    • 轻量级:Mybatis是一个轻量级的持久层框架,体积小,易于集成
    • 灵活性:Mybatis支持原生SQL语句,灵活性高
    • 易用性:Mybatis使用简单,易于上手

1.2 Mybatis快速入门

  • 步骤:使用Mybatis查询所有用户数据
    • 准备工作(创建springboot工程、数据库表user、实体类User)
    • 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
    • 编写SQL语句(注解/XML)

步骤:

  1. 准备工作(创建springboot工程、数据库表user、实体类User)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- user表
create table user(
id int unsigned primary key auto_increment comment 'ID',
name varchar(100) comment '姓名',
age tinyint unsigned comment '年龄',
gender tinyint unsigned comment '性别, 1:男, 2:女',
phone varchar(11) comment '手机号'
) comment '用户表';

insert into user(id, name, age, gender, phone) VALUES (null,'白眉鹰王',55,'1','18800000000');
insert into user(id, name, age, gender, phone) VALUES (null,'金毛狮王',45,'1','18800000001');
insert into user(id, name, age, gender, phone) VALUES (null,'青翼蝠王',38,'1','18800000002');
insert into user(id, name, age, gender, phone) VALUES (null,'紫衫龙王',42,'2','18800000003');
insert into user(id, name, age, gender, phone) VALUES (null,'光明左使',37,'1','18800000004');
insert into user(id, name, age, gender, phone) VALUES (null,'光明右使',48,'1','18800000005');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// User.java
package com.itheima.pojo;

public class User {

private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;


public User() {
}

public User(Integer id, String name, Short age, Short gender, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.phone = phone;
}

/**
* 获取
* @return id
*/
public Integer getId() {
return id;
}

/**
* 设置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* 获取
* @return age
*/
public Short getAge() {
return age;
}

/**
* 设置
* @param age
*/
public void setAge(Short age) {
this.age = age;
}

/**
* 获取
* @return gender
*/
public Short getGender() {
return gender;
}

/**
* 设置
* @param gender
*/
public void setGender(Short gender) {
this.gender = gender;
}

/**
* 获取
* @return phone
*/
public String getPhone() {
return phone;
}

/**
* 设置
* @param phone
*/
public void setPhone(String phone) {
this.phone = phone;
}

public String toString() {
return "User{id = " + id + ", name = " + name + ", age = " + age + ", gender = " + gender + ", phone = " + phone + "}";
}
}
  1. 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
  • 引入Mybatis的相关依赖:创建项目时勾选Mybatis framework和Mybatis Driver
  • 配置数据库连接信息
1
2
3
4
5
6
7
8
# 驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
# 连接数据库的用户名
spring.datasource.username=root
# 连接数据库的密码
spring.datasource.password=1234
  1. 编写SQL语句(注解/XML)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// UserMapper.java
package com.itheima.mapper;


import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper // 在运行时会自动生成该接口的实现类对象(代理对象),并且将该对象交给IOC容器管理
public interface UserMapper {

// 查询全部用户信息
@Select("select * from user")
public List<User> list();
}
  1. 单元测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.itheima;

import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {

@Autowired
private UserMapper userMapper;

@Test
public void testListUser() {
List<User> userlist = userMapper.list();
userlist.stream().forEach(System.out::println);
}

}

1.3 IDEA配置MySQL

  • 在IDEA中配置MySQL数据库连接
    • 打开Database窗口,点击加号,选择Data Source -> MySQL
    • 输入数据库连接信息,点击Test Connection测试连接是否成功
    • 点击OK保存配置

1.4 JDBC与数据库连接池

  • JDBC(Java Database Connectivity)是Java语言中用于连接和操作数据库的API
  • 数据库连接池
    • 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
    • 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
    • 释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
  • 连接池的优点
    • 资源重用
    • 提升系统响应速度
    • 避免数据库连接遗漏
  • 标准接口:DataSource
    • 官方(sun)提供的数据库连接池接口,由第三方组织实现此接口
    • 功能:获取连接(Connection getConnection() throes SQLException)
    • 常见产品:
      • DBCP(Apache Commons DBCP)
      • C3P0
      • Druid(阿里巴巴开源的数据库连接池项目)
      • HikariCP(springboot默认)
  • 切换Druid连接池
    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.9</version>
    </dependency>
    • 配置文件
    1
    2
    3
    4
    5
    # Druid连接池配置
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
    spring.datasource.username=root
    spring.datasource.password=1234

1.5 lombok

  • Lombok是一个实用的Java类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高效率。
注解 作用
@Getter/@Setter 为所有的属性提供get/set方法
@ToString 会给类自动生成易阅读的toString方法
@EqualsAndHashCode 根据类所拥有的非静态字段自动重写equals方法和hashCode方法
@Data 提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode)
@NoArgsConstructor 为实体类生成无参的构造器方法
@AllArgsConstructor 为实体类生成除了static修饰的字段之外带有各参数的构造器方法
  • 引入lombok依赖
1
2
3
4
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// User.java
package com.itheima.pojo;

import lombok.*;

//@Getter
//@Setter
//@ToString
//@EqualsAndHashCode

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;

}

2025.05.20

二、Mybatis基础操作

  • 案例需求:根据资料中提供的《tlias智能学习辅助系统》页面原型及需求,完成员工管理的需求开发
  • 功能列表:
    • 查询
      • 根据主键ID查询
      • 条件查询
    • 新增
    • 更新
    • 删除
      • 根据主键ID删除
      • 根据主键ID批量删除

2.1 Mybatis环境准备

  • 准备工作
    • 准备数据库表emp
    • 创建一个新的springboot工程, 选择引入对应的起步依赖(mybatis、mysq|驱动、lombok)
    • application.properties中引入数据库连接信息
    • 创建对应的实体类Emp (实体类属性采用驼峰命名)
    • 准备Mapper接口EmpMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
-- 部门管理
create table dept(
id int unsigned primary key auto_increment comment '主键ID',
name varchar(10) not null unique comment '部门名称',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '部门表';

insert into dept (id, name, create_time, update_time) values(1,'学工部',now(),now()),(2,'教研部',now(),now()),(3,'咨询部',now(),now()), (4,'就业部',now(),now()),(5,'人事部',now(),now());



-- 员工管理
create table emp (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) default '123456' comment '密码',
name varchar(10) not null comment '姓名',
gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
image varchar(300) comment '图像',
job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',
entrydate date comment '入职时间',
dept_id int unsigned comment '部门ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '员工表';

INSERT INTO emp
(id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time) VALUES
(1,'jinyong','123456','金庸',1,'1.jpg',4,'2000-01-01',2,now(),now()),
(2,'zhangwuji','123456','张无忌',1,'2.jpg',2,'2015-01-01',2,now(),now()),
(3,'yangxiao','123456','杨逍',1,'3.jpg',2,'2008-05-01',2,now(),now()),
(4,'weiyixiao','123456','韦一笑',1,'4.jpg',2,'2007-01-01',2,now(),now()),
(5,'changyuchun','123456','常遇春',1,'5.jpg',2,'2012-12-05',2,now(),now()),
(6,'xiaozhao','123456','小昭',2,'6.jpg',3,'2013-09-05',1,now(),now()),
(7,'jixiaofu','123456','纪晓芙',2,'7.jpg',1,'2005-08-01',1,now(),now()),
(8,'zhouzhiruo','123456','周芷若',2,'8.jpg',1,'2014-11-09',1,now(),now()),
(9,'dingminjun','123456','丁敏君',2,'9.jpg',1,'2011-03-11',1,now(),now()),
(10,'zhaomin','123456','赵敏',2,'10.jpg',1,'2013-09-05',1,now(),now()),
(11,'luzhangke','123456','鹿杖客',1,'11.jpg',5,'2007-02-01',3,now(),now()),
(12,'hebiweng','123456','鹤笔翁',1,'12.jpg',5,'2008-08-18',3,now(),now()),
(13,'fangdongbai','123456','方东白',1,'13.jpg',5,'2012-11-01',3,now(),now()),
(14,'zhangsanfeng','123456','张三丰',1,'14.jpg',2,'2002-08-01',2,now(),now()),
(15,'yulianzhou','123456','俞莲舟',1,'15.jpg',2,'2011-05-01',2,now(),now()),
(16,'songyuanqiao','123456','宋远桥',1,'16.jpg',2,'2010-01-01',2,now(),now()),
(17,'chenyouliang','123456','陈友谅',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());

2.2 预编译SQL

  • 预编译SQL优势
    • 效率更高
    • 更安全(防止SQL注入)
      • SQL注入是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法
  • 参数占位符
    • #{…}
      • 执行SQL时会将#{…}替换为?,生成预编译SQL,会自动设置参数值
      • 使用时机:参数传递都使用#{…}
    • ${…}
      • 拼接SQL,直接将参数拼接在SQL语句中,存在SQL注入问题
      • 使用时机:如果对表名、列表进行动态设置时使用

2.3 Mybatis的删除

  • SQL语句
1
2
-- 删除数据
delete from emp where id = 17;
  • Mapper接口
1
2
3
// EmpMapper.java
@Delete("delete from emp where id = #{id}")
public void Delete(Integer id);
  • 注意:如果mapper接口方法形参只有一个普通类型的参数,#{}里面的属性名可以随便写,如#{id}、#{value}
  • 日志输出
    • 可以在application.properties中打开mybatis的日志,并指定输出到控制台
    1
    2
    # mybatis日志输出到控制台
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

2025.05.21

2.4 Mybatis的新增

  • SQL语句
1
2
3
-- 新增数据
insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)
VALUES ('Tom', '汤姆', 1, '1.jpg', 1, '2005-01-01', 1, now(), now());
  • Mapper接口
1
2
3
4
5
// EmpMapper.java
// 新增员工
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
"VALUES (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
public void insert(Emp emp);
  • 新增(主键返回)
    • 在数据添加成功后,需要获取插入数据库数据的主键。如添加套餐数据时,还需要维护套餐菜品关系表数据
    • 实现:使用@Options注解
    1
    2
    3
    4
    5
    6
    7
    8
    // 新增员工
    // @Options注解:会自动将生成的主键值赋值给emp对象的id属性
    // useGeneratedKeys:是否使用主键返回
    // keyProperty:主键返回的属性名
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
    "VALUES (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
    public void insert(Emp emp);

2.5 Mybatis的更新

  • SQL语句
1
2
3
-- 更新数据
update emp set username = '', name = '', gender = '', image = '', job = '', entrydate = '', dept_id = '', update_time = ''
where id = 1;
  • Mapper接口
1
2
3
4
// EmpMapper.java
// 更新员工信息
@Update("update emp set username = #{username}, name = #{name}, gender = #{gender}, image = #{image}, job = #{job}, entrydate = #{entrydate}, dept_id = #{deptId}, update_time = #{updateTime} where id = #{id}")
public void update(Emp emp);

2.6 Mybatis的查询

2.6.1 根据ID查询

  • SQL语句
1
2
-- 查询数据(根据ID查询)
select * from emp where id = 1;
  • Mapper接口
1
2
3
4
// EmpMapper.java
// 根据ID返回员工信息
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
  • 数据封装
    • 实体类属性名和数据库表查询返回的字段名一致, mybatis会自动封装
    • 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装
  • 解决方法
    • 方案一:给字段起别名,让别名与实体类属性一致
    1
    2
    3
    @Select("select id, username, password, name, gender, image, job, entrydate, " +
    "dept_id deptID, create_time createTime, update_time updateTime from emp where id = #{id}")
    public Emp getById(Integer id);
    • 方案二:使用@Results,@Result注解手动映射封装
    1
    2
    3
    4
    5
    6
    7
    @Results({
    @Result(column = "dept_id", property = "deptID"),
    @Result(column = "create_time", property = "createTime"),
    @Result(column = "update_time", property = "updateTime")
    })
    @Select("select * from emp where id = #{id}")
    public Emp getById(Integer id);
  • 方案三:开启mybatis的驼峰命名自动映射开关
    • 在application.properties中添加配置
    1
    2
    # 开启驼峰命名自动映射开关
    mybatis.configuration.map-underscore-to-camel-case=true

2.6.2 根据条件查询

  • SQL语句
1
2
3
4
5
-- 根据条件查询员工
-- 1 根据输入的员工姓名、员工性别、入职时间搜索满足条件的员工信息
-- 2 其中员工姓名,支持模糊匹配;性别进行精确查询;入职时间进行范围查询
-- 3 并对查询的结果,根据最后修改时间进行倒序排序
select * from emp where name like '%张%' and gender = 1 and entrydate between '2010-01-01' and '2020-01-01' order by update_time desc;
  • Mapper接口
1
2
3
4
5
6
// EmpMapper.java
// 条件查询
@Select("select * from emp " +
"where name like concat('%', #{name}, '%') and gender = #{gender} and entrydate between #{begin} and #{end} " +
"order by update_time desc;")
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
  • 参数名说明

三、XML映射文件

3.1 XML映射文件规范

  • 规范
    • XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
    • XML映射文件的namespace属性为Mapper接口全限定名一致
    • XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致
  • XML约束:https://mybatis.p2hp.com/getting-started.html

四、动态SQL

动态SQL:随着用户的输入或外部条件的变化而变化的SQL语句

4.1 动态SQL-if

  • <if>
    • 作用:根据条件判断是否拼接SQL语句,where元素只会在子元素有内容的情况下才插入where子句,而且会自动去除子句开头的AND或OR
    • 语法:
    1
    2
    3
    <if test="属性名 != null">
    SQL语句
    </if>
  • <where>
    • 作用:动态拼接where条件
    • 语法:
    1
    2
    3
    4
    5
    <where>
    <if test="属性名 != null">
    SQL语句
    </if>
    </where>
  • <set>
    • 作用:动态拼接set条件,用在update语句中,可以自动删除多余的逗号
    • 语法:
    1
    2
    3
    4
    5
    <set>
    <if test="属性名 != null">
    SQL语句
    </if>
    </set>

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--resultType:单条记录所封装的类型-->
<select id="list" resultType="com.itheima.pojo.Emp">
select *
from emp
<where>
<if test="name != null">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>

4.2 动态SQL-foreach

  • <foreach>
    • 作用:动态拼接SQL语句,遍历集合或数组
    • 语法:
    1
    2
    3
    <foreach collection="集合名" item="集合遍历出的元素/项" separator="分隔符" open="遍历开始前拼接的片段" close="遍历结束后拼接的片段">
    SQL语句
    </foreach>

示例:

1
2
3
4
5
6
7
<!--批量删除-->
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>

4.3 动态SQL-sql include

  • <sql>
    • 作用:定义一个SQL片段,便于复用
    • 语法:
    1
    2
    3
    <sql id="sql片段id">
    SQL语句
    </sql>
  • <include>
    • 作用:引用SQL片段
    • 语法:
    1
    <include refid="sql片段id" />

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<sql id="commonSelect">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time
from emp
</sql>

<!--resultType:单条记录所封装的类型-->
<select id="list" resultType="com.itheima.pojo.Emp">
<!--select *-->
<!--from emp-->
<include refid="commonSelect"/>
<where>
<if test="name != null">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>