阶段一:DB和MP之间的交互
官网:http://mp.baomidou.com/
参考教程:http://mp.baomidou.com/guide/
快速开始参考:http://mp.baomidou.com/guide/quick-start.html
简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化 开发、提高效率而生 。
使用及测试
初始化数据库
新建表:User DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT ‘主键ID’, name VARCHAR(30) NULL DEFAULT NULL COMMENT ‘姓名’, age INT(11) NULL DEFAULT NULL COMMENT ‘年龄’, email VARCHAR(50) NULL DEFAULT NULL COMMENT ‘邮箱’, PRIMARY KEY (id)
);
初始化表中数据 DELETE FROM user; INSERT INTO user (id, name, age, email) VALUES (1, ‘Jone’, 18, ‘test1@baomidou.com’), (2, ‘Jack’, 20, ‘test2@baomidou.com’), (3, ‘Tom’, 28, ‘test3@baomidou.com’), (4, ‘Sandy’, 21, ‘test4@baomidou.com’), (5, ‘Billie’, 24, ‘test5@baomidou.com’);
<a name="rcnw1"></a>
### 初始化工程
1. 使用 Spring Initializr 初始化一个 Spring Boot 工程
Group:com.atguigu <br />Artifact:academy<br />版本:2.2.1.RELEASE
2. 引入依赖:[pom.xml](https://www.yuque.com/attachments/yuque/0/2021/xml/22137958/1630546960137-63298049-bc51-42cd-843a-3c125c1f3cb4.xml?_lake_card=%7B%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2021%2Fxml%2F22137958%2F1630546960137-63298049-bc51-42cd-843a-3c125c1f3cb4.xml%22%2C%22name%22%3A%22pom.xml%22%2C%22size%22%3A2171%2C%22type%22%3A%22text%2Fxml%22%2C%22ext%22%3A%22xml%22%2C%22status%22%3A%22done%22%2C%22taskId%22%3A%22uaca21c5f-5570-4a36-b422-4a2dbdb6c75%22%2C%22taskType%22%3A%22upload%22%2C%22id%22%3A%22ua3e88b0e%22%2C%22card%22%3A%22file%22%7D)
3. 使用MP之后不要再引入Mybatis和Mybatis-Spring
3. 修改配置
```yaml
#com.mysql.cj.jdbc.Driver 在 jdbc 8 中 建议使用这个驱动,
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#修改自己的数据库名,springboot2.1集成8.0版本的jdbc需加上后缀?...
spring.datasource.url=jdbc:mysql://localhost:3306/guliacademy?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=021633
启动类加上@MapperScan进行接口扫描,对Mapper文件夹内的文件扫描
@SpringBootApplication @MapperScan("com.atguigu.mybatisplus.mapper") public class MybatisPlusApplication { ...... }
加上@MapperScan 扫描接口包地址:
编写实体类
@Data public class User { private Long id; private String name; private Integer age; private String email; }
编写UserMapper接口,继承于BaseMapper
- 使用的是mybatis-plus的特性,BaseMapper中封装了很多通用的方法
public interface UserMapper extends BaseMapper<User> { }
把实体类User加到<>中:
BaseMapper中封装的方法:
- 使用的是mybatis-plus的特性,BaseMapper中封装了很多通用的方法
编写测试类
添加测试类AcademyApplicationTests,进行功能测试:
使用MP实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!@RunWith(SpringRunner.class) @SpringBootTest class AcademyApplicationTests { @Autowired UserMapper userMapper; @Test void contextLoads() { System.out.println(("----- selectAll method test ------")); System.out.println(("----- 开始打印输出 ------")); //UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper //所以不填写就是无任何条件 List<User> users = userMapper.selectList(null); users.forEach(System.out::println); System.out.println(("----- 打印输出结束 ------")); } }
引入userMapper后,会出现报红,但是程序依旧可以执行的情况:
IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执 行。 为了避免报错,可以在 dao 层 的接口上添加 @Repository 注 解开启sql日志
#mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
关于insert
主键策略¥¥
参考资料:https://www.cnblogs.com/haoxinyue/p/5208136.html
问题引入:当插入一条数据时,并没有填写其id,但是插入的表格中却显示了数据的id。这时因为MP默认的主键策略,使用雪花算法给插入的数据生成一个19位的id
1432903183880945665为mp自动生成, MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID
- 主键自增
要想实现主键的自增,可以在属性上引入注解 @TableId(type = IdType.AUTO )或在配置文件中进行全局配置
自增的不合理之处:在分表的场景下。需要得到上张表的最后一个值,来确定下张表的首id值
- UUID策略
UUID:每次自动生成随机唯一的值。分表时生成新表无需得到上一张表的最后一个值,但这种方式无法排序
- Redis
Redis实现:用Redis的原子操作 INCR和INCRBY来实现。redis是单线程的,因此也可以生成全局唯一ID
关于update
自动填充¥¥
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。 我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:
实现步骤
在表User中添加两个字段 create_time、update_time (写死的字段名),在实体类User中添加对应的属性并加上注解并自定义实现类重写方法,让操作更加明显
在字段对应的属性上加上注解,并在元对象处理器接口中重写方法,别忘加上@component把类交给spring
乐观锁¥¥![image.png](/uploads/projects/ruid@kma5xb/ce7b8373ac8988dfa619c5c1986e1675.png)
- 使用场景:
若不加锁,则多线程在修改公共数据时,最后提交的数据会把之前线程对此数据的修改都覆盖。这样实际上仅有最后一个线程在进行修改。乐观锁就是要解决这一丢失更新的问题。
- 使用方法:
在MP中使用乐观锁只需要三步:在表中加version字段,在实体类中加verison属性和@version注解,在接口中配置插件
在在元数据类中配置设置verison初始值为1,在插入时默认写入初始值1。这样乐观锁就会根据version的值实现锁机制。
乐观锁插件可以直接写入启动类中,但是这样会配置混乱,不易管理配置,建议新建配置类写入配置类中
- 写入乐观锁插件的配置类
- 实现细节
- 下面是加了乐观锁的测试代码
若在修改更新的过程中,数据被其他线程修改更新,version值会改变,这样在原修改更新过程中的verison值前后就不一致了,即使执行了更新操作,数据库中的数据也不会更新了。version不一致,程序可以正常执行但是数据不会修改变化。这里的另一个线程的修改导致的version变化用get,set方法模拟。
关于select
分页查询¥
复杂条件查询¥
条件构造器Wapper¥¥¥
多使用继承于Wapper的QueryWapper,其中包含了Wapper的方法也存在自己特有的方法,功能更强大。
下面是测试代码以及用到的方法,红色为MP中转换的sql语句。 在此仅示例两个,其他方法查文档
@Test
public void testDelete() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNull("name")
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}
@Test
public void testSelectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}
关于delete
逻辑删除¥¥
- 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
- 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍 旧能看到此条数据记录 。应用场景:垃圾回收站中的还原
实现步骤:
同乐观锁类似,在数据库添加deleted字段,在实体类添加deleted属性并加上注解@TableLogic,在元数据配置类添加自动填充默认值0;在配置类中加入插件
wrapper位null,表示查询没有附加条件
逻辑删除在MP的sql中其实是一条更新语句,把deleted从0置位1.
性能分析
性能分析拦截器,用于输出每条 SQL 语句及其执行时间 SQL 性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题 。
在配置类中进行配置即可,根据设置的最大运行时间进行操作。可以设置环境开启,比如dev环境下才开启
阶段二:后端讲师管理模块CRUD
数据库初始化
使用的sql
guli_acl.sqlguli_cms.sqlguli_edu.sqlguli_order.sqlguli_statistics.sqlguli_ucenter.sql
使用guli_edu.sql创建关于讲师管理模块的表
数据库的规约
项目结构
项目配置
配置pom.xml,集成类型
Properties和dependencyManagement来共同确定依赖,前者确定版本号,后者确定要导入的依赖
子模块中使用dependency来继承父模块定义好的依赖并下载,此时就不需要再确定版本号
父工程中不写代码
初步测试
![image.png](/uploads/projects/ruid@kma5xb/62bbba66c43a6aa45611ba6760bfbb92.png)
先测试环境,
查,
编写controller,注入service,编写具体方法
加@scanMapper到配置类上
最后测试
两个工具
MP的代码生成器¥¥
把生成器,其实是一个类放到test文件夹中使用。CodeGenerator.java
- 第一部分主要文件输出改路径和主键策略
- 第二部分,把代码生成器原有的数据库配置,改成自己的数据库
- 注意:包配置是生成三级结构以及各个子包名,输出路径最好用绝对路径
Swagger2 ¥¥¥
浏览器无法测试delete提交,浏览器只能get,post,所以需使用swagger来测试delete接口
SwaggerConfig.java
接口测试工具,delete操作无法在浏览器中直接测试,使用swagger可以完成,上面是swagger的配置步骤
因为swagger是接口测试工具,在整个开发过程中都需要使用,所以就把它放在了common模块中配置,本项目中又在common模块下建立了service-base模块来存放关于service的共同配置,swagger配置类存入其中。
使用:在模块中引入配置类所在的service-base模块,再在使用的地方加载配置类,定义配置扫描范围
下面是swagger配置类中的相关信息与网页中的信息对应关系
这是swagger的注解描述:
三个统一
统一返回结果
此部分放在common模块中定义,先创建接口并定义状态返回码,再定义统一返回数据的模板类
编写自定义返回码接口
编写返回结果类
在使用的模块中,如service模块中添加依赖,让通用返回类所在的模块从属于要使用的类
下面是使用统一返回模板前后:
统一异常处理
在common模块中编写异常处理类GlobalExceptionHandler,需要加上注解@ControllerAdvice。
自定义异常需要手动抛出,前两种是系统遇到异常自动抛出 。
全局异常处理
特定异常处理
对指定的异常ArithmeticException进行特定的处理
自定义异常处理
需要编写自定义的异常:GuliException
再在异常处理类中进行自定义异常的编写
统一日志处理
默认是INFO,运行时输出INFO信息,改为WARN后,运行时只输出WARN级别之上的信息
使用logback输出日志到文件中,不仅仅在控制台输出
logback-spring.xml
注:自定义的文件中 有文件输出路径,有dev,test环境开启设置,不仅输出日志还输出异常语句
四个操作
预备知识:条件构造器进阶
之前MP章节的学习只是自定义了条件构造器,里面的内容是已经写死的,不是用户根据需求输入的。构造器语法在之前已经说明,此处不加解释。
我们需要的Wrapper,在具体操作中根据用户输入的条件返回结果,下面是分页条件查询的截图。
- 若是直接写在controller中,则先写条件查询的属性,再写一写判断逻辑,因为是组合查可以写入判断值为空的逻辑,最后把条件值写入page方法的参数中
- 若把条件构造器放到service层,则第一新建数据类,把要传输的数封装在类中。第二在接口中新增一个自定义的方法,方法的参数是封装在对象中的要传输的数据并在实现类中重写此方法,再其中新建构造器,对将要传输的值进行自定义的逻辑操作,最后查询。
总结:条件传递在对象里,对象传递到接口中
增
增操作主要涉及自动填充。增加数据库字段,类中属性及注解,设置元数据默认值。最后 在启动类上加配置扫描范围
前端把数据传输到后台,若有的属性未被传输,则默认值为自动填充值。其中又涉及主键递增策略
删
- 单id删
- 多id删
- map删
- 逻辑删
逻辑删:在数据库中加字段,在实体类属性上注解,在配置类中加插件,最后编写controller,在swagger中测试
- 自定义条件删
改
单id查
多id改,按自定义条件改,相关逻辑可按之前方法补充:条件封装在对象里,对象封装在接口中
查
单id查,多id查省略
分页查和组合条件分页查如下:
- 无条件的分页查:在配置类中加插件,编写controller
- 按条件分页查:在分页查的基础上加上自定义的条件—-EduteacherQuery
先创建page对象用来存储查到的数据,再构建条件wrapper对象,其要自定义条件,最后把wrapper作为放入接口的方法的参数。
3. 关于@RequestBody
使用前是以键值对来传送数据
使用后是根据json格式上传构造器所需要的数据
注:@ResponseBody是以json从后端返回数据