Mybatis基本操作
一、准备工作
1. 需求说明
-
根据资料中提供的《tlias智能学习辅助系统》页面原型及需求,完成员工管理的需求开发。
- 资料来源:微信公众号—>黑马程序员—>获取资源—>百度网盘下载
2. 准备数据表
-- 部门管理
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());
3. 创建SpringBoot工程
-
创建一个新的springboot工程,选择引入对应的起步依赖(mybatis、mysql驱动、lombok)
4. 配置application.properties
-
application.properties中引入数据库连接信息
#驱动类名称 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=12345
5. 创建实体类
-
创建 emp表对应的实体类 Emp(实体类属性采用驼峰命名)
package com.app.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.time.LocalDate; import java.time.LocalDateTime; @Data //综合生成:getter、setter、equals、hashcode、toString方法 @NoArgsConstructor //生成无参构造 @AllArgsConstructor//生成全参构造 public class Emp { private Integer id; //主键ID private String username; //用户名 private String password; //密码 private String name; //姓名 private Short gender; //性别, 说明: 1 男, 2 女 private String image; //图像 private Short job; //职位, 说明: 1 班主任, 2 讲师, 3 学工主管, 4 教研主管, 5 咨询师 private LocalDate entrydate; //入职日期 private Integer deptId; //部门ID private LocalDateTime createTime; //创建时间 private LocalDateTime updateTime; //修改时间 }
6. 准备Mapper接口
-
准备Mapper接口 EmpMapper
package com.app.mapper; import org.apache.ibatis.annotations.Mapper; @Mapper //作用:运行时自动创建该接口的对象(代理对象),并将该对象交个IOC容器管理 public interface EmpMapper { }
二、删除
1. 需求
-
根据主键删除数据
2. 分析
(1) SQL语句分析
-
SQL:
-- 根据主键ID删除 delete from emp where id=17;
3.Mybatis实现
-
Mapper接口:
package com.app.mapper; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Mapper; @Mapper //作用:运行时自动创建该接口的对象(代理对象),并将该对象交个IOC容器管理 public interface EmpMapper { //根据主键ID删除: #{变量名} -- 动态获取要删除的数据的主键ID @Delete("delete from emp where id = #{id}") public void deleteById(Integer id); }
-
测试:
package com.app; import com.app.mapper.EmpMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class SpringbootMybatisCrudApplicationTests { @Autowired private EmpMapper empMapper; @Test public void testDeleteById() { empMapper.deleteById(13); } }
4. 注意事项
(1) 参数命名
- 如果mapper接口方法 形参只有一个普通类型的参数,#{…} 里面的属性名可以随便写,如:#{id}、#{value}。
- 不过还是建议写与表结构字段一样,增加可读性。
(2) 返回值
- delete语句执行完毕后,是有返回值的,这个返回值:此次操作影响的记录数。
-
Mapper接口:
package com.app.mapper; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Mapper; @Mapper //作用:运行时自动创建该接口的对象(代理对象),并将该对象交个IOC容器管理 public interface EmpMapper { //根据主键ID删除: #{变量名} -- 动态获取要删除的数据的主键ID //(1) 无返回值(常用) // @Delete("delete from emp where id = #{id}") // public void deleteById(Integer id); //(2) 有返回值(了解) @Delete("delete from emp where id = #{id}") public int deleteById(Integer id); }
-
测试:
三、日志输出
1. 配置
-
可以在application.properties中,配置mybatis的日志,并指定输出到控制台。
#指定mybatis输出日志的位置,输出控制台 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
2. 输出日志
-
输出日志:
3. 预编译SQL
(1) 介绍
- ?:参数占位符
- 最终在执行时,会将这条SQL语句 以及 参数16 都发送给数据库
- 数据库在执行这条SQL语句时,会使用 传递过来的参数 来替换掉 ?,最终来删除 id=16 的员工信息
(2) 优势
-
为何要采用这种问号占位符的预编译SQL语句?而不是直接将参数拼接在SQL语句中?
-
优势:
①. 性能更高
②. 更安全(防止SQL注入)
-
(3) SQL执行流程
-
SQL语句执行流程:
-
在Java项目中,编写了一条SQL语句,要想执行,需要连接上数据库,然后将SQL语句发送给MySQL数据库服务器
-
而这条SQL语句发送给MySQL数据库服务器之后,数据库并不是立即来执行的,而是要经历一系列的过程:
-
为了提高效率,在数据库中会将优化编译后的SQL缓存起来:
-
缓存的目的:
- 下一次再执行SQL语句时会先检查缓存中是否有编译好的SQL语句,如果有就不需要再执行这一系列的操作了,可以直接去执行SQL语句。
- 否则,又需要执行这一系列的操作,再把编译后的SQL语句缓存起来。
-
(4) 优势解释
① 性能更高:
-
非预编译SQL语句:
- 因为这三条sql语句的id的值不一样,因此每执行一条SQL语句,都要进行一系列的操作,总共要进行3次编译
-
预编译SQL语句:
- 因为使用了 ? 符号来进行占位,这样只需要编译1次,之后的执行,只需要将 参数 与 占位符 进行替换之后即可执行。
- 这样就不用每次执行都要进行一系列的操作了,性能大大提高。
② 更安全(防止SQL注入)
-
SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行 攻击 的方法。
4. 参数占位符(面试)
(1) #{…}
-
执行SQL时,会将 #{…} 替换为 ?,生成预编译SQL,会自动设置参数值。
- 使用时机:参数传递,都使用 #{…}
(2) ${…}
-
拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题。
- 使用时机:如果对表名、列表进行动态设置时使用。
四、新增
1. 需求
-
插入数据
2. 分析
(1) 字段分析
-
新增员工信息时,哪些字段需要插入,哪些字段不需要:
(2) SQL语句分析
-
SQL语句:
-- 插入数据(新增员工) insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values ('Tony','托尼',1,'Tony.jpg',3,'2004-09-01',2,now(),now());
3. Mybatis实现
-
Mapper接口:
package com.app.mapper; import com.app.pojo.Emp; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; @Mapper //作用:运行时自动创建该接口的对象(代理对象),并将该对象交个IOC容器管理 public interface EmpMapper { //根据主键ID删除: #{变量名} -- 动态获取要删除的数据的主键ID // (1) 无返回值(常用) @Delete("delete from emp where id = ${id}") public void deleteById(Integer id); //(2) 有返回值(了解) // @Delete("delete from emp where id = #{id}") // public int deleteById(Integer id); //新增员工 @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" + //" values ('Tony','托尼',1,'Tony.jpg',3,'2004-09-01',2,now(),now())") //死编码 " values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})") public void insertEmp(Emp emp); }
-
测试:
五、主键返回
1. 需求
- 在数据添加成功后,需要获取插入数据库数据的主键。
- 如:添加套餐数据时,还需要维护套餐菜品关系表数据。
2. 实现
package com.app.mapper;
import com.app.pojo.Emp;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
@Mapper //作用:运行时自动创建该接口的对象(代理对象),并将该对象交个IOC容器管理
public interface EmpMapper {
//根据主键ID删除: #{变量名} -- 动态获取要删除的数据的主键ID
// (1) 无返回值(常用)
@Delete("delete from emp where id = ${id}")
public void deleteById(Integer id);
//(2) 有返回值(了解)
// @Delete("delete from emp where id = #{id}")
// public int deleteById(Integer id);
//新增员工
@Options(keyProperty = "id", useGeneratedKeys = true) //会自动将生成的主键值,赋值给emp对象的id属性
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
//" values ('Tony','托尼',1,'Tony.jpg',3,'2004-09-01',2,now(),now())") //死编码
" values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
public void insertEmp(Emp emp);
}
六、更新
1. 需求
-
根据主键ID修改数据
2. 分析
(1) 字段分析
-
修改员工信息时,哪些字段需要修改,哪些字段不需要:
(2) SQL语句分析
- 根据主键更新数据
-- 修改数据(更新员工信息)
update emp
set username = 'zhangfei1',
name = '张飞1',
gender = 1,
image = 'zhangfei1.jpg',
job = 3,
entrydate = '2005-05-05',
dept_id = 3,
update_time = now()
where id = 19;
3. Mybatis实现
-
Mapper接口:
package com.app.mapper; import com.app.pojo.Emp; import org.apache.ibatis.annotations.*; @Mapper //作用:运行时自动创建该接口的对象(代理对象),并将该对象交个IOC容器管理 public interface EmpMapper { //根据主键ID删除: #{变量名} -- 动态获取要删除的数据的主键ID // (1) 无返回值(常用) @Delete("delete from emp where id = #{id}") public void deleteById(Integer id); //(2) 有返回值(了解) // @Delete("delete from emp where id = #{id}") // public int deleteById(Integer id); //新增员工信息 @Options(keyProperty = "id", useGeneratedKeys = true) //会自动将生成的主键值,赋值给emp对象的id属性 @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" + //" values ('Tony','托尼',1,'Tony.jpg',3,'2004-09-01',2,now(),now())") //死编码 " values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})") public void insertEmp(Emp emp); //更新员工信息 @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 updateEmp(Emp emp); }
-
测试:
七、查询
1. 需求
-
根据ID查询数据
2. 分析
-
SQL语句
-- 根据主键ID查询 select * from emp where id = 23;
3. Mybatis实现
-
Mapper接口:
-
测试:
4. 封装问题
-
会发现:部门ID、创建时间、修改时间,这三个字段查询不到值,都为 null
- 这是Mybatis的数据封装问题
5. 数据封装
(1) 介绍
- 实体类属性名 和 数据库表查询返回的字段名一致,mybatis会自动封装。
- 如果实体类属性名 和 数据库表查询返回的字段名 不一致,不能自动封装。
(2) 解决方案
① 字段起别名
-
起别名:在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样。
② 手动结果映射
-
手动结果映射:通过 @Results及@Result 进行手动结果映射。
③ 开启驼峰命名
-
开启驼峰命名:如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射。
#开启驼峰命名自动映射,即从数据库字段名 a_column 映射到Java 属性名 aColumn。 mybatis.configuration.map-underscore-to-camel-case=true
八、条件查询
1. 需求
-
根据指定条件查询数据
-
需求文档:
2. 分析
-
SQL语句
-- 根据指定条件查询 select * from emp where name like '%张%' -- 员工姓名,支持模糊匹配 and gender = 1 -- 性别 进行精确查询 and entrydate between '2010-01-01' and '2020-01-01' -- 入职时间 进行范围查询 order by update_time desc ; -- 根据最后修改时间进行倒序排序
3. Mybatis实现
-
Mapper接口
-
测试:
-
问题:
4. concat()函数
1. 介绍
- 字符串拼接函数:concat()
- 将多个字符串拼接成一个字符串。
2. 优化
-
SQL:
-
Mybatis:
-
Mapper接口:
-
测试:
- 推荐这种方式!!
-
九、参数名说明
1.springBoot的1.x版本(早期)
-
在springBoot的 1.x版本 /单独使用mybatis(早期的版本)
-
编码时,形参列表是这样的:
-
编译后的字节码文件,形参列表是这样的:
-
原因:由于 在springBoot的 1.x版本 以及 单独使用mybatis 的时候,在对 Mapper接口 进行编译的过程中并不会保留方法的形参名称,最终变成 var1、var2 … 这样的。因此 #{参数名} 与 形参列表 是 对应不起来的
-
因此需要加上一个注解:@Param(指定要获取的参数)
-
2. springBoot的2.x版本(目前)
-
在springBoot的2.x版本(目前)
-
编码时,形参列表是这样的:
-
编译后的字节码文件,形参列表是这样的:
- 原因:由于 在springBoot的 2.x版本,在对 Mapper接口 进行编译的过程中会保留方法的形参名称。因此 #{参数名} 与 形参列表 是 可以对应起来的
- 因此不需要加上注解:@Param(指定要获取的参数)
-