Mybatis Plus入门
MP是 | MybatisPlus,简称MP,是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变。MP为简化开发、提高效率而生。 它已经封装好了单表curd方法,我们直接调用这些方法就能实现单表CURD。 注意:MP仅仅提供了单表的CURD。 如果要实现多表联查,就只能自己动手实现了,按Mybatis的方式写 是dao层的框架 |
小结 |
MP使用入门
步骤说明 | MP是dao层操作数据库的框架,它的使用步骤和Mybatis类似但要简单的多 MP的使用步骤:
|
1.准备数据库表 | 连接本机的MySQL,执行《资料/mp_db.sql》,或者执行以下脚本代码: create database if not exists mp_db character set utf8; |
2.准备创建一个SpringBoot工程,准备MybatisPlus环境MP项目环境 | 这里准备了两种方式,在实际开发中都可能会使用。大家习惯哪一种就用哪一种 建议使用手动创建方式 方式一:手动创建工程使用idea创建一个maven工程,不用选择任何Artifact骨架,设置好工程坐标即可。然后按照如下步骤准备MP的开发环境: 1) 添加依赖 在工程的pom.xml里添加依赖: <parent> <properties> <dependencies> 2) 创建配置文件 在工程的resources文件夹里创建配置文件 spring: 3) 创建引导类 在工程的java文件夹里创建引导类 @SpringBootApplication 方式二:使用SpringInitializr1) 创建Project 选择Spring Initializr方式 2) 设置工程信息 3) 选择依赖 注意:SpringBoot版本选择2.x.x;选择依赖时只要勾选MySQL Driver即可
4) 添加依赖 上一步中,我们仅仅是选择了一个数据库驱动依赖,还没有MybatisPlus的依赖。所以还需要修改pom.xml,添加MybatisPlus的坐标: <dependency> |
3.使用MybatisPlus | 创建实体类 在工程的java文件夹里创建实体类 package com.myps.pojo; public class User { private Long id; private String userName; private String password; private Integer age; private String tel; private String sex; //get和set方法,省略了,大家手动添加上即可 //toString方法,省略了,大家手动添加上即可 } 创建Mapper 在
|
4.测试MybatisPlus | 创建一个单元测试类,测试MP的功能,代码如下: package com.myps; import com.myps.mapper.UserMapper; import com.myps.pojo.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class Demo01MpTest { @Autowired private UserMapper userMapper; @Test public void test1(){ User user = userMapper.selectById(1); System.out.println(user); } }
|
小结 | 什么是MP: 是dao层的框架,是对Mybatis的增强,在Mybatis基础上只增强不改变 MP提供好一整套的单表CURD功能,我们可以直接使用 MP的准备: 添加MP的依赖坐标,MySQL的驱动包 配置数据库的地址 MP的使用: 1. 准备实体类 2. 创建dao层的接口 要求继承BaseMapper<实体类> 要求接口上添加注解@Mapper |
MP简单CURD【重点】
简单CURD操作
CRUD | 说明MP内置了强大的BaseMapper,它已经提供好了单表CURD功能:只要我们的Mapper接口继承了BaseMapper,就可以直接使用整套的单表CURD功能了。 常用的基本curd方法有:
| ||||||||||||||||
查询 |
分页查询需单独分析: | ||||||||||||||||
新增 | Ctrl+p | ||||||||||||||||
修改 |
| ||||||||||||||||
删除 | |||||||||||||||||
小结 | 根据id查询:selectById(id值)
|
示例代码
import com.myps.mapper.UserMapper;
import com.myps.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;
/**
* Mp提供的CRUD的方法
* 所有查询的方法:以select开头
* 所有新增的方法:以insert开头
* 所有修改的方法:以update开头
* 所有删除的方法:以delete开头
*/
@SpringBootTest
public class Demo02CrudTest {
@Autowired
private UserMapper userMapper;
/**
* 查询id为1的用户
*/
@Test
public void test1Select() {
User user = userMapper.selectById(1);
System.out.println(user);
}
/**
* 查询所有用户 列表
*/
@Test
public void test1Selects() {
List<User> users = userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
/**
* 查询数量
*/
@Test
public void test1SelectCount() {
Integer count = userMapper.selectCount(null);
System.out.println("总数量是:" + count);
}
/**
* 插入数据
* 插入时MP默认会自动生成id值
* 插入后MP会自动把id值设置到实体类对象的主键属性上
*/
@Test
public void test2Insert() {
User user = new User();
user.setUserName("凉介");
user.setAge(25);
user.setPassword("123456");
user.setTel("18698946596");
user.setSex("男");
System.out.println("插入之前的user:" + user);
//执行完成 insert之后,Mp会自动把主键值设置到 id属性上
int insert = userMapper.insert(user);
System.out.println("影响的行数 :" + insert);
System.out.println("插入之后的user:" + user);
}
/**
* 修改数据
*/
@Test
public void test3updateById() {
// User user = new User();
// user.setUserName("小鑫");
// user.setId(1655891470097350657L);
//先查:一般不会new一个对象修改
User user = userMapper.selectById(1655891470097350657L);
user.setUserName("李阔");
//updateById的底层是根据id修改的 update ... set ... where id = xxx
int i = userMapper.updateById(user);
System.out.println("影响的行数 :" + i);
}
/**
* 删除数据(根据id删除)
*/
@Test
public void test4DeleteById() {
int i = userMapper.deleteById(1655891470097350657L);
System.out.println("影响行数:" + i);
}
}
三、MP使用详解
MP使用详情 |
| ||||||||||||||
MP日志 | 开启MP日志打印SQL语句问题说明在刚刚所有的CURD操作中,都没有在控制台中打印SQL及其执行过程。如果程序执行出错,没有日志信息会对解决bug造成干扰。 解决方案修改配置文件 mybatis-plus: 再次执行SQL语句,在控制台里就有日志信息了 | ||||||||||||||
取消SpringBoot启动日志 | 问题说明我们发现,控制台里输出的日志内容特别多。其中大部分内容是SpringBoot启动时的一些日志。这些日志并没有太大的实际作用,我们可以想办法取消掉 解决方案在resources目录下新建一个文件,名称是 <?xml version="1.0" encoding="UTF-8" ?> 更多关于logback的信息,可以参考:logback.xml常用配置详解 - 简书 修改后效果: | ||||||||||||||
取消日志中的banner图标 | 问题说明如果想要继续精简不必要的日志,我们可以取消输出SpringBoot的banner和MybatisPlus的banner 解决方案修改配置文件 spring: 输出SpringBoot的图标
修改后: | ||||||||||||||
Lombok的使用 | Lombok介绍我们在编写JavaBean类的时候,有大量冗余的模板式的代码, 像:get方法、set方法、toString方法、构造方法等等。 这些方法没有什么技术含量,但是在开发中又是必不可少的。 使用Lombok可以帮我们解决这些问题。 Lombok项目是一个java库,它简化了JavaBean的编写, 避免了大量冗余的、样板式的代码,不需要再写getter、setter或equals方法, 只要有一个注解即可。 它提供了以下常用注解:
Lombok的使用添加Lombok依赖坐标修改工程的pom.xml,添加lombok的依赖坐标,如下: <dependency> Lombok使用示例改造一个我们的User类(后续所有的JavaBean,我们都可以这样写):
@Data | ||||||||||||||
小结 | Lombok是干什么的: 简化JavaBean的编写,可以减少JavaBean里的冗余的、模板式的代码 Lombok的用法: 1. 导入lombok的依赖坐标 2. 哪个实体类要用Lombok,就在类上添加注解: @Data:会生成get、set、无参构造、toString、hashCode、equals等等方法 @NoArgsConstructor:生成无参构造 @AllArgsConstructor:生成全参构造 | ||||||||||||||
MP的实体类注解 | 在刚刚的入门案例中,我们的User类里并没有特殊的属性、特殊的用法。但是实际开发场景往往比较复杂,MP提供了一些注解,可以供我们处理这些复杂的场景。
| ||||||||||||||
小结 | 1. @TableField注解:用于设置JavaBean属性 和 表里的字段 的对应关系。用于非主键字段上 2. @TableName注解:用于设置 实体类名 和 数据库表名 的对应关系 3. @TableId注解:用于设置主键字段与属性的映射关系,还可以设置主键的生成策略 |
四、 MP条件查询【重点】
上午回顾 | ||||||||||||||||||||||||||||||||||||||||
介绍 | 条件查询介绍以前使用Mybatis开发时,遇到动态条件的查询,就需要我们在xml文件里使用各种标签实现SQL语句的动态拼接。而MP把这些封装成了Java的API,我们可以以编程的形式完成SQL语句的构建并实现复杂的多条件查询。 而复杂的多条件查询的关键在于查询条件的构造,为此MP提供了查询条件抽象类: MP提供的所有条件式的操作,都需要传入这个类型的参数。比如:
在实际开发中,有时候会把QueryWrapper转换成LambdaQueryWrapper,两者混合使用。 | |||||||||||||||||||||||||||||||||||||||
开发环境准备 | 为了避免前边代码的干扰,我们准备一个新的工程,步骤略 实体类:User @Data UserMapper @Mapper | |||||||||||||||||||||||||||||||||||||||
QueryWrapper的使用 | 使用入门能够使用QueryWrapper或者LambdaQueryWrapper实现条件查询 需求查询年龄大于20岁的用户,只查询用户的id、姓名、年龄,结果按年龄降序排列 示例 | |||||||||||||||||||||||||||||||||||||||
设置查询条件 | 说明如果要使用
示例 | |||||||||||||||||||||||||||||||||||||||
MP分页查询【重点】
掌握MP的分页查询步骤和查询方法 | |||||||||||||||
介绍 | 之前我们要实现分页查询,要么在mybatis中引入pageHelper插件,要么完全手动实现分页查询。 而MybatisPlus本身就内置分页插件,不需要再额外导入任何插件,也不需要我们再手动实现了。 | ||||||||||||||
分页API | MP的Mapper提供的分页查询方法是:
| ||||||||||||||
使用步骤 |
| ||||||||||||||
示例配置分页插件 | import com.baomidou.mybatisplus.annotation.DbType; @Configuration | ||||||||||||||
执行分页查询 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import java.util.List; @SpringBootTest @Test //执行分页查询 //得到分页查询结果 | ||||||||||||||
小结 | 分页查询: 1. 先配置分页插件。如果不配置分页插件,将会是查询所有数据 如果需要使用,直接拷贝粘贴,不建议手写 2. 再调用分页查询的方法: IPage iPage = selectPage(new Page(页码,查询几条), wraper查询条件); iPage.getTotal()得到总数量 iPage.getPages()得到总页数 iPage.getRecord()得到数据列表 | ||||||||||||||
六、MP的DML操作
| |
MP的批量操作说明 | 在实际开发中,通常需要批量操作。例如:批量删除、批量下单等等,如果下图所示 MP提供了批量操作的一些方法,常用的有:
|
示例 | import com.itheima.mapper.UserMapper; import java.util.ArrayList; @SpringBootTest @Test userMapper.deleteBatchIds(ids); @Test List<User> users = userMapper.selectBatchIds(ids); |
MP的逻辑删除说明 | 如果需要删除一条数据,开发中往往有两种方案可以实现:
在实际开发中,逻辑删除使用的更多一些。所以MP也提供了逻辑删除的支持,帮助我们更方便的实现逻辑删除 使用步骤:
MP逻辑删除的本质是:
|
示例 | 1 增加状态字段 执行SQL语句: 2 增加属性并加@TableLogic注解 4 测试 |
MP的乐观锁 | |
说明 | 随着互联网的发展,特别是国内互联网人数的增加,数据量、并发量也随之增加,并发访问控制的问题也越发凸显,例如:秒杀。如果一条数据被多个线程并发修改数据,就需要对数据加锁,避免其它线程修改这条数据。 MP已经帮我们实现了乐观锁,它的使用步骤非常简单:
MP的乐观锁实现逻辑也非常简单:
|
示例 | 1 增加版本号字段 执行以下SQL语句 2 修改实体类 3 配置乐观锁插件 在配置类( 4 测试 |
小结 | 批量操作: deleteBatchIds(id集合):根据id集合批量删除 selectBatchIds(id集合):根据id集合批量查询 逻辑删除: 1. 修改表,增加一个状态字段,用于标记这个数据是否被删除。字段名称要见名知意,比如叫deleted 2. 修改实体类,增加对应的属性,并且在属性上加注解@TableLogic 3. 修改配置文件,告诉MP删除状态的值:已删除是什么值,未删除是什么值 乐观锁: 如果数据涉及了查询和修改操作,为了避免其它线程对同一数据的干扰,可以加锁 悲观锁:只要一查询数据,就对这条数据加排它锁,其它线程的任何操作都不能访问。安全性更高,但是性能低 乐观锁:查询数据时,根据版本号进行控制。如果查询后修改前,数据被其它线程修改了,版本号会变。后边的修改操作会被放弃 MP的乐观锁的实现步骤 1. 修改表,增加一个版本号字段 2. 修改实体类,增加对应的属性,并且要给属性加注解@Version 3. 配置乐观锁插件 4. 使用:先查询再更新,乐观锁会自动生效。 |
七、MP代码生成器【了解】
说明 | MP可以帮我们有效的减轻开发中的工作量:所有的单表CURD完全不用自己编写了,直接使用MP就行。但是这还不够,因为我们还需要编写实体类、Mapper接口、Service、Controller代码。 为了更进一步的减轻我们的工作量,MP提供了代码生成器,可以根据数据库表,直接生成好实体类、Mapper接口,甚至Service和Controller。 |
示例 | 1 新建一个project创建一个新的project:
创建引导类:略 2 编写代码生成器1 添加依赖
2 编写生成器代码 精简版 |
精简版
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; public class MpCodeGenerator { public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/mp_db?useUnicode=true&useSSL=false&characterEncoding=utf8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); mpg.setDataSource(dsc); //开始执行 mpg.execute(); } }
详细版
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; public class MpCodeGenerator { public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); // 代码文件生成到哪:当前工程的src/main/java里 gc.setOutputDir("src/main/java"); // 代码作者 gc.setAuthor("liuyp"); // 生成后是否直接打开文件夹 gc.setOpen(false); // 是否覆盖原代码文件 gc.setFileOverride(true); // 主键生成策略 gc.setIdType(IdType.AUTO); mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/mp_db?useUnicode=true&useSSL=false&characterEncoding=utf8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); // 设置父包名。与代码所在文件夹不冲突 pc.setParent("com.itheima"); // 设置实体类所在包名。父包名+此包名,即是实体类所在位置 pc.setEntity("pojo"); // 设置Mapper所在包名。父包名+此包名,即是Mapper接口所在位置 pc.setMapper("mapper"); mpg.setPackageInfo(pc); // 策略配置 StrategyConfig strategy = new StrategyConfig(); // 实体类是否使用Lombok strategy.setEntityLombokModel(true); // 是否生成Rest风格的Controller类 strategy.setRestControllerStyle(true); mpg.setStrategy(strategy); mpg.execute(); } }
简答题
问题一
请简述MybatisPlus与Mybatis之间的关系是什么?
MybatisPlus是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发与提高效率。
问题二
逻辑删除与物理删除的区别是什么?
1.物理删除执行的是delete语句,会将数据从数据库表中直接删除掉。
2.逻辑删除执行的是update语句,会在数据库表中添加一列字段,该字段用来标识数据是否被删除,所以逻辑删除只需要修改该列的值即可完成逻辑删除,如果使用逻辑删除需注意在查询的时候需要把已被删除的数据排除掉
问题三
MybatisPlus标识实体类对应的表、实体类主键字段与其他字段对应的注解分别是什么?
1. 标识实体类与对应表之间的关系使用@TableName
2. 标识实体类主键使用@TableId
3. 标识实体类其他非主键字段使用@TableField
问题四
请简述MybatisPlus常见的主键生成策略有哪些并说说该如何修改其策略?
1.AUTO采用数据库ID主键自增策略
2.INPUT手动设置主键值
3.ASSIGN_ID使用雪花算法生成主键ID
4.ASSIGN_UUID使用UUID作为主键ID
5.NONE无状态,该类型为未设置主键类型
设置主键策略有两种方式
方式一:使用注解设置
@TableId(type = IdType.AUTO)
方式二:使用配置文件的方式,在application.yml添加如下配置
mybatis-plus:
global-config:
db-config:
id-type: auto #主键策略,auto自动增长
问题五
简述MybatisPlus如何构建条件查询?
MybatisPlus支持两种查询方式,分别是QueryWrapper和LambdaQueryWrapper。
QueryWrapper的构建方式如下:
1.构建查询对象,QueryWrapper<实体类> qw = new QueryWrapper<>();
2.构建查询条件,如qw.eq(boolean condition, R column, Object val)
condition若为true则添加条件查询若为false则不添加该条件
column为查询的字段的名称
val为查询的值,最终构建的条件为 where column = val
LambdaQueryWrapper的构建方式如下:
1.构建查询对象,LambdaQueryWrapper<实体类> lqw = new LambdaQueryWrapper<>();
2.构建查询条件,如lqw.eq(boolean condition, R column, Object val)
condition若为true则添加条件查询若为false则不添加该条件
column为查询的字段,编写方式为 实体类::getXxx
val:为查询值,最终构建的条件为 where column = val
其中eq是等值查询,如果想构建其他条件查询,则可参考官网来构建,官网地址为:
https://baomidou.com/pages/10c804/
问题六
聊一聊MybatisPlus内部是如何简化Mybatis对表进行增删改查?
1.MybatisPlus的主旨是通过操作实体对象来操作数据库表,从而减少基础增删改查方法及对应SQL语句的编写;
2.MybatisPlus使用@TableName、@TableField、@TableId建立了实体类与表、实体类属性与表的列以及标识主键字段;
3.编写Mapper接口继承BaseMapper<实体类>,当调用其方法,比如 int insert(T entity); T即为传入的实体类,MybatisPlus会将insert方法翻译成 insert into ,根据实体类与表之间的关系获取到对应的表名,根据实体类的属性与表中列的关系获取表的列名,从传入的实体类对象中获取需要添加的数据,最终可以组装成对应的sql语句,即 insert into tableName (column1,column2,...) values(?,?,...),然后执行SQL语句完成数据库表的新增功能,而且这些操作都是框架内部自动完成;
4.综上所述,对于表基本的增删改查就可以直接通过调用BaseMapper接口中提供的方法进行完成,省略了编写方法与对应的SQL语句,达到简化开发提供效率。
训练目标
能够灵活的建立数据库表与实体类之间的对应关系
能够使用MybatisPlus实现表的增删改查
能够通过MybatisPlus官方文档学会使用MybatisPlus的自动填充功能
能够使用MybatisPlus实现数据库表的分页条件排序列表查询
需求描述
现有一电商的后台项目需要你参与开发,并将其中的商品模块交予你进行维护。商品表及项目环境都已经在素材中提供,需要按照需求完成对应功能的开发与测试,最终实现的效果图展示如下:
题目一
为了满足新商品上线,需要提供一个商品表的
新增商品
功能。题目二
项目运行过程中,发现商品ID为(66)的商品存在恶意刷单的情况,需要将其进行下架处理。
题目三
双十一为了吸引客户,需要进行打折促销,现将品牌(brand)为
华为
,分类(category)为手机
的商品价格调至200元同时库存调为100个。题目四
在对商品进行维护的时候,其中创建时间(createTime)与修改时间(updateTime)需要实时更新,现在面临的问题是如果项目中所有的表都包含有这两个字段,每个表维护的时候都需要进行设置,存在重复工作和遗忘的风险。现在需要你根据MybatisPlus官网提供的自动填充功能
https://mp.baomidou.com/guide/auto-fill-metainfo.html
将上述问题解决掉。题目五
用户需要进行商品搜索,可以提供按照商品的品牌(brand模糊)、分类(category等值)和价格(price区间)进行分页条件查询,并且按照价格进行升序。
素材及素材说明
作业素材的目录下有项目的基础环境,需将其部署到IDEA中,并将数据库连接信息修改成自己本地环境
作业素材的目录下有tb_item.sql文件,将数据库表与对应的数据导入到本地数据库mybatisplus_db中
提示
创建模型类的时候需要建立模型类与表之间的对应关系,其中isAD需要额外关注下
使用MybatisPlus完成数据库表的增删改查
需求中使用到MybatisPlus的自动填充,课程中未讲到,需要借助于官方文档按照步骤实现
在使用MybatisPlus实现分页查询的时候,需要配置分页插件
在查询的时候需要将已下架或者删除的商品排除掉
思路分析
将项目导入到IDEA中并将数据库及对应的表创建好
根据数据库表创建对应的模型类,在模型类中需要检查模型类与表,属性与列名以及主键的标识
编写Mapper接口使其继承BaseMapper
编写的Mapper接口需要被框架扫描到,有两种实现方式
调用Mapper接口提供的方法完成数据库表的增删改查
需求一:调用Mapper接口提供的insert方法完成功能
需求二与需求三:调用Mapper接口提供的update方法完成功能
需求四:需要使用@TableField(fill = FieldFill.INSERT)与MetaObjectHandler
需求五:需要配置PaginationInterceptor并调用selectPage方法进行查询
步骤一:根据tb_item表创建对应的模型类
import com.baomidou.mybatisplus.annotation.TableField;
import java.util.Date;
public class Item {
private Long id; //主键ID
private String name; //商品名称
private Long price; //商品价格
private Integer stock; //商品库存
private String image; //商品图片地址
private String category; //商品分类
private String brand; //商品品牌
private Integer sold; //商品销量
private Integer commentCount; //商品评论数
@TableField("isAD")
/**
* 此处需要注意:MybatisPlus默认是支持驼峰模式,
* isAD按照驼峰模式找对应的列名为 is_a_d和数据库表的列不一致
* 所以@TableField不能省略
*/
private Boolean isAD; //是否是首页推广广告,true/false
private Integer status; //商品状态 1-正常,2-下架,3-删除
private Date createTime; //创建时间
private Date updateTime;//更新时间
//setter...getter...toString...省略
}
步骤二:创建Mapper接口继承BaseMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.Item;
public interface ItemMapper extends BaseMapper<Item> {
}
注意:BaseMapper后面的泛型不能省略
步骤三:让框架能够扫描到Mapper接口
方式一:接口上添加 @Mapper注解,此方式需要在每个Mapper接口上都需要添加注解
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.Item;
@Mapper
public interface ItemMapper extends BaseMapper<Item> {
}
方式二:引导类上添加 @MapperScan,此方式只需要添加一次即可,但是需要将所有的Mapper接口放在被扫描的包中
@SpringBootApplication
@MapperScan("com.itheima.mapper")//指定扫描Mapper接口的包路径
public class MpTaskApp {
public static void main(String[] args) {
SpringApplication.run(MpTaskApp.class, args);
}
}
步骤四:创建测试类,注入ItemMapper
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {
@Autowired
private ItemMapper itemMapper;
}
需求一:完成商品表的新增功能
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {
@Autowired
private ItemMapper itemMapper;
@Test
public void addItem(){
Item item = new Item();
item.setName("华为P50");
item.setPrice(668800L); //设置价格单位是分
item.setImage("https://img11.360buyimg.com/n1/s450x450_jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg");
item.setCategory("手机");
item.setBrand("华为");
item.setCreateTime(new Date());
item.setUpdateTime(new Date());
//下面属性都有默认值,可以不用设置
// item.setStock(1000);
// item.setSold(0);
// item.setCommentCount(0);
// item.setAD(true);
// item.setStatus(1);
//最终sql语句:INSERT INTO tb_item ( name, price, image, category, brand, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
itemMapper.insert(item);//新增
}
}
需求二:将id为66的商品进行下架
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {
@Autowired
private ItemMapper itemMapper;
@Test
public void offShelf(){
Item item = new Item();
item.setId(66L);
item.setStatus(2); //设置状态为2-下架
item.setUpdateTime(new Date());//更新修改时间
//最终的SQL语句:UPDATE tb_item SET status=?, update_time=? WHERE id=?
itemMapper.updateById(item); //根据ID进行修改
}
}
需求三:将所有华为手机的价格调至200元,库存调至100个
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {
@Autowired
private ItemMapper itemMapper;
@Test
public void editItem(){
UpdateWrapper<Item> updateWrapper = new UpdateWrapper<>();
//设置修改条件
updateWrapper.eq("brand","华为");
updateWrapper.eq("category","手机");
//设置修改内容
updateWrapper.set("price",20000);//单位为分,200元需要换算成分
updateWrapper.set("stock",100); //库存调至100个
updateWrapper.set("update_time",new Date()); //记录商品修改时间
//最终的sql语句为:UPDATE tb_item SET price=?,stock=?,update_time=? WHERE (brand = ? AND category = ?)
itemMapper.update(null,updateWrapper);
}
}
需求四:在新增与修改的时候自动填充createTime与updateTime
(1)修改Item模型类,在createTime与updateTime属性上添加@TableField注解用以标识自动填充字段
import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import java.util.Date; public class Item { private Long id; private String name; private Long price; private Integer stock; private String image; private String category; private String brand; private Integer sold; private Integer commentCount; @TableField("isAD") private Boolean isAD; private Integer status; //FieldFill.INSERT指定为新增的时候自动填充 @TableField(fill = FieldFill.INSERT) private Date createTime; //FieldFill.INSERT_UPDATE指定为新增和修改的时候自动填充 @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; //setter...getter...toString...省略 }
(2)编写类实现MetaObjectHandler接口,设定填充内容
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.util.Date; @Component //让Spring来管理该类 public class MyMetaObjectHandler implements MetaObjectHandler{ /** * 在执行新增的时候会自动执行的方法,在该方法中为createTime与updateTime进行设值 * @param metaObject 元数据 */ @Override public void insertFill(MetaObject metaObject) { /** * 新增填充,参数一:元数据,参数二:填充的字段属性名称,参数三:填充值的类型,参数四:填充具体的值 */ this.strictInsertFill(metaObject,"createTime",Date.class,new Date()); /** * 修改填充,参数一:元数据,参数二:填充的字段属性名称,参数三:填充值的类型,参数四:填充具体的值 */ this.strictUpdateFill(metaObject,"updateTime",Date.class,new Date()); } /** * 在执行修改的时候会自动执行的方法,在该方法中为updateTime进行设置 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { /** * 修改填充,参数一:元数据,参数二:填充的字段属性名称,参数三:填充值的类型,参数四:填充具体的值 */ this.strictUpdateFill(metaObject,"updateTime",Date.class,new Date()); } }
(3)再次执行新增或修改的时候,就不需要手动设置createTime与updateTime,操作成功后数据库表中自动填充了createTime与updateTime
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Date; @SpringBootTest(classes = MpTaskApp.class) public class MpTest { @Autowired private ItemMapper itemMapper; @Test public void addItem(){ Item item = new Item(); item.setName("华为P51"); item.setPrice(668800L); //设置价格单位是分 item.setImage("https://img11.360buyimg.com/n1/s450x450_jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg"); item.setCategory("手机"); item.setBrand("华为"); //将设置时间的内容去掉,执行成功后createTime与updateTime依然会有值 //item.setCreateTime(new Date()); //item.setUpdateTime(new Date()); //下面属性都有默认值,可以不用设置 // item.setStock(1000); // item.setSold(0); // item.setCommentCount(0); // item.setAD(true); // item.setStatus(1); //最终sql语句:INSERT INTO tb_item ( name, price, image, category, brand, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? ) itemMapper.insert(item); //新增 } @Test public void offShelf(){ Item item = new Item(); item.setId(66L); item.setStatus(2); //设置状态为2-下架 //将设置时间的内容去掉,执行成功后updateTime依然会更新 //item.setUpdateTime(new Date());//更新修改时间 //最终的SQL语句:UPDATE tb_item SET status=?, update_time=? WHERE id=? itemMapper.updateById(item); //根据ID进行修改 } }
(4)如果所有的表中都有createTime和updateTime两个属性,为了避免重复工作,可以将createTime和updateTime抽取到一个POJO类中,然后让其他实体类都继承该POJO,就不需要在每个实体类声明这两个属性,减少重复工作
import java.util.Date; public class BasePojo { private Date createTime; private Date updateTime; //setter...getter...toString...省略 } package com.itheima.domain; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import java.util.Date; public class Item extends BasePojo{ private Long id; //主键ID private String name; //商品名称 private Long price; //商品价格 private Integer stock; //商品库存 private String image; //商品图片地址 private String category; //商品分类 private String brand; //商品品牌 private Integer sold; //商品销量 private Integer commentCount; //商品评论数 @TableField("isAD") /** * 此处需要注意:MybatisPlus默认是支持驼峰模式, * isAD按照驼峰模式找对应的列名为 is_a_d和数据库表的列不一致 * 所以@TableField不能省略 */ private Boolean isAD; //是否是首页推广广告,true/false private Integer status; //商品状态 1-正常,2-下架,3-删除 // 因为继承了BasePojo,所以下面的属性与对应的方法都可以省略不写 // @TableField(fill = FieldFill.INSERT) //FieldFill.INSERT指定为新增的时候自动填充 // private Date createTime; //创建时间 // @TableField(fill = FieldFill.INSERT_UPDATE) //FieldFill.INSERT_UPDATE指定为新增和修改的时候自动填充 // private Date updateTime;//更新时间 // public Date getCreateTime() { // return createTime; // } // // public void setCreateTime(Date createTime) { // this.createTime = createTime; // } // // public Date getUpdateTime() { // return updateTime; // } // // public void setUpdateTime(Date updateTime) { // this.updateTime = updateTime; // } //getter...setter...toString...省略 }
需求五:实现商品表的分页条件排序列表查询
(1)参考官方文档配置分页插件
https://mp.baomidou.com/guide/page.html
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //因为使用的是MYSQL,所以此处需要修改为DbType.MYSQL interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
(2)进行分页条件排序列表查询
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.itheima.domain.Item; import com.itheima.mapper.ItemMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.util.StringUtils; import java.util.Date; import java.util.List; @SpringBootTest(classes = MpTaskApp.class) public class MpTest { @Autowired private ItemMapper itemMapper; @Test public void testFindByCondition(){ //虚拟搜索条件,这些条件可有可无 String brandName = "小米"; String category = "平板"; Long minPrice = 100000L; //1000元 Long maxPrice = 1000000L; //10000元 //分页参数 Integer page = 1; //当前页码 Integer pagesize=2; //每页显示记录条数 //构建查询条件 QueryWrapper<Item> wrapper = new QueryWrapper<>(); wrapper.like(brandName!=null &&!"".equals(brandName),"brand",brandName); // brand like %?% wrapper.eq(category!=null && !"".equals(category),"category",category); //category =? wrapper.ge(minPrice!=null,"price",minPrice);// price >= minPrice wrapper.le(maxPrice!=null,"price",maxPrice); //price <= maxPrice //需要查询状态为正常的,将下架和删除的商品排除掉 wrapper.eq("status",1); //status = 1 //构建按照价格price升序 wrapper.orderByAsc("price"); //构建分页对象 IPage<Item> iPage = new Page<>(page,pagesize); iPage = itemMapper.selectPage(iPage,wrapper); long total = iPage.getTotal(); System.out.println("总记录数为:"+total); //当前页的列表集合 List<Item> records = iPage.getRecords(); for (Item record : records) { System.out.println(record); } } }