第4章-第2节
一、知识点
Mybatis-Plus、Lambda
二、目标
-
理解什么是Mybatis-Plus
-
理解Mybatis和Mybatis-Plus的区别
-
学会使用Mybatis-Plus的CRUD
-
条件构造器的使用
-
分页查询的使用
三、内容分析
-
重点
-
学会使用Mybatis-Plus的CRUD
-
什么是查询过滤,有什么用
-
条件构造器的使用
-
分页查询的使用
-
-
难点
-
学会使用Mybatis-Plus的CRUD
-
条件构造器的使用
-
四、内容
1、简介
1.1 概念
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
1.2 特性
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作,BaseMapper
-
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求,简单的CRUD操作不用自己编写。
-
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
-
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
-
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
-
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
-
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
-
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
1.3 和Mybatis的区别
-
MyBatis 是一种持久层框架,用来连接数据库并对其进行增删改查操作的开源框架,底层就是一个 JDBC 封装的组件,访问数据库的 sql 语句存放于 mapper (或Dao) 包下的 xml 配置文件中。
-
Mybatis-Plus 是 MyBatis 的一个增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus 已经封装好了一些 CRUD 方法,因此不需要再写 xml 了,仅仅通过少量配置即可实现单表大部分的CRUD操作。使用时直接调用这些方法就行。
2、基本使用
2.1 新建一个项目引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
2.2 配置yml
# 配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot-mysql?userSSL=false;serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis-plus:
# mapper配置文件
mapper-locations: classpath:mapper/*.xml
# resultType别名,没有这个配置resultType包名要写全,配置后只要写类名
type-aliases-package: com.example.springboot01.entity
configuration:
# 日志的配置,直接添加即可,内部有自带配置项
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2.3 配置启动类
@MapperScan("com.example.demo.mapper") // 填mapper/dao路径
2.4 创建Student实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
...
}
2.5 创建mapper接口
// BaseMapper后面的泛型写的是需要返回的实体类
@Repository
public interface StudentMapper extends BaseMapper<Student> {
// 基本的CRUD已经写好了
}
2.6 调用
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentMapper mapper;
@GetMapping("/list")
public List<Student> queryAll() {
// 调用baseMapper中封装好的方法直接查询即可
return mapper.selectList(null);
}
}
3、Service的使用
为了让我们的开发更符合一般的JAVA开发规范,我们一般会使用Service来进一步封装
3.1 创建IStudentService接口实现IService<T> 接口
public interface IStudentService extends IService<Student> {
}
3.2 创建StudentServiceImpl实现类继承ServiceImpl<M extends BaseMapper<T>, T>
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements IStudentService {
// 实现了通用Service的CRUD方法
}
3.3 Controller调用
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService service;
@GetMapping
public List<Student> queryAll(){
return service.list();
}
}
4、常用方法
官方网址:https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%A3
4.1 Mapper CRUD 接口
-
Insert
// 插入一条记录 int insert(T entity);
-
Delete
// 根据 entity 条件,删除记录 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 删除(根据ID 批量删除) int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 ID 删除 int deleteById(Serializable id); // 根据 columnMap 条件,删除记录 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
-
Update
// 根据 whereWrapper 条件,更新记录 int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper); // 根据 ID 修改 int updateById(@Param(Constants.ENTITY) T entity);
-
select
// 根据 ID 查询 T selectById(Serializable id); // 根据 entity 条件,查询一条记录 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据ID 批量查询) List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 entity 条件,查询全部记录 List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据 columnMap 条件) List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 Wrapper 条件,查询全部记录 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 entity 条件,查询全部记录(并翻页) IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录(并翻页) IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询总记录数 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
4.2 Service CRUD 接口
-
Save
// 插入一条记录(选择字段,策略插入) boolean save(T entity); // 插入(批量) boolean saveBatch(Collection<T> entityList); // 插入(批量) boolean saveBatch(Collection<T> entityList, int batchSize);
-
SaveOrUpdate
// TableId 注解存在更新记录,否插入一条记录 boolean saveOrUpdate(T entity); // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法 boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper); // 批量修改插入 boolean saveOrUpdateBatch(Collection<T> entityList); // 批量修改插入 boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
-
Remove
// 根据 entity 条件,删除记录 boolean remove(Wrapper<T> queryWrapper); // 根据 ID 删除 boolean removeById(Serializable id); // 根据 columnMap 条件,删除记录 boolean removeByMap(Map<String, Object> columnMap); // 删除(根据ID 批量删除) boolean removeByIds(Collection<? extends Serializable> idList);
-
Update
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset boolean update(Wrapper<T> updateWrapper); // 根据 whereWrapper 条件,更新记录 boolean update(T updateEntity, Wrapper<T> whereWrapper); // 根据 ID 选择修改 boolean updateById(T entity); // 根据ID 批量更新 boolean updateBatchById(Collection<T> entityList); // 根据ID 批量更新 boolean updateBatchById(Collection<T> entityList, int batchSize);
-
Get
// 根据 ID 查询 T getById(Serializable id); // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1") T getOne(Wrapper<T> queryWrapper); // 根据 Wrapper,查询一条记录 T getOne(Wrapper<T> queryWrapper, boolean throwEx); // 根据 Wrapper,查询一条记录 Map<String, Object> getMap(Wrapper<T> queryWrapper); // 根据 Wrapper,查询一条记录 <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
-
List
// 查询所有 List<T> list(); // 查询列表 List<T> list(Wrapper<T> queryWrapper); // 查询(根据ID 批量查询) Collection<T> listByIds(Collection<? extends Serializable> idList); // 查询(根据 columnMap 条件) Collection<T> listByMap(Map<String, Object> columnMap); // 查询所有列表 List<Map<String, Object>> listMaps(); // 查询列表 List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper); // 查询全部记录 List<Object> listObjs(); // 查询全部记录 <V> List<V> listObjs(Function<? super Object, V> mapper); // 根据 Wrapper 条件,查询全部记录 List<Object> listObjs(Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录 <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
-
Page
// 无条件分页查询 IPage<T> page(IPage<T> page); // 条件分页查询 IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper); // 无条件分页查询 IPage<Map<String, Object>> pageMaps(IPage<T> page); // 条件分页查询 IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
-
Count
// 查询总记录数 int count(); // 根据 Wrapper 条件,查询总记录数 int count(Wrapper<T> queryWrapper);
5、注解
官网地址:https://baomidou.com/pages/223848/#tablename
-
@TableName
-
描述:表名注解,标识实体类对应的表
-
使用位置:实体类
// 表名这个类对应的是数据库的哪一张表,如果没有写则默认是使用类名,也就是user表 // 这个时候这个User的实体类对的是sys_user表 @TableName("sys_user") public class User {}
-
-
@Tableld
-
描述:主键注解
-
使用位置:实体类主键字段
@TableName("sys_user") public class User { @TableId private Long id; private String name; private Integer age; private String email; }
属性 类型 必须指定 默认值 描述 value String 否 "" 主键字段名 type Enum 否 IdType.NONE 指定主键类型 IdType
值 描述 AUTO 数据库 ID 自增 NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) INPUT insert 前自行 set 主键值 ASSIGN_ID 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口 IdentifierGenerator
的方法nextId
(默认实现类为DefaultIdentifierGenerator
雪花算法)ASSIGN_UUID 分配 UUID,主键类型为 String(since 3.3.0),使用接口 IdentifierGenerator
的方法nextUUID
(默认 default 方法)
-
-
@TableField
-
描述:字段注解(非主键)
@TableName("sys_user") public class User { @TableId private Long id; @TableField("nickname") private String name; private Integer age; private String email; }
-
6、条件构造器
官网地址:https://baomidou.com/pages/10c804/
6.1 AbstractWrapper
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类 用于生成 sql 的 where 条件
-
eq
等于 =
eq(R column, Object val) eq(boolean condition, R column, Object val) 例: eq("name", "老王")--->name = '老王'
-
ne
不等于 <>
ne(R column, Object val) ne(boolean condition, R column, Object val) 例: ne("name", "老王")--->name <> '老王'
-
like
LIKE '%值%'
like(R column, Object val) like(boolean condition, R column, Object val) 例: like("name", "王")--->name like '%王%'
-
or
-
拼接 OR
主动调用
or
表示紧接着下一个方法不是用and
连接!(不调用or
则默认为使用and
连接)or() or(boolean condition) 例: eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'
-
OR 嵌套
or(Consumer<Param> consumer) or(boolean condition, Consumer<Param> consumer) 例: or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')
-
-
and
-
AND 嵌套
and(Consumer<Param> consumer) and(boolean condition, Consumer<Param> consumer) 例: and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')
-
6.2 QueryWrapper
继承自 AbstractWrapper,所以可以使用 AbstractWrapper 的方法,常用于查询条件的设置
@GetMapping("/conditions")
public List<Student> queryByConditions(Student student){
QueryWrapper<Student> wrapper = new QueryWrapper<>();
// 添加年龄的判断,第一个参数为true的时候才会拼接到字符串中,第二个参数是数据库的字段名,第三个参数是值
wrapper.eq(student.getAge() != null,"age", student.getAge());
// 添加姓名的模糊查询
wrapper.like(StringUtils.isNotEmpty(student.getName()),"name", student.getName());
return service.list(wrapper);
}
6.3 LambdaQueryWrapper
使用lambda语法写条件
6.3.1 lambda表达式概念
Lambda表达式是Java8中的新特性,编码时,我们一般尽可能轻量级的将代码封装为数据,传统的解决方案是通过接口和实现类(匿名内部类)实现,他是函数式编程思想的一种体现,我们不用关注对象是什么,只要关注对对象做什么操作就好了
6.3.2 基本格式和使用
(参数列表)->{代码块}
-
线程
// 之前这么写 new Thread(new Runnable() { @Override public void run() { System.out.println("启动线程"); } }).start(); // 使用lambda new Thread(() -> { System.out.println("Lambda启动线程"); }).start();
-
自己定义
-
定义接口(只能写一个方法 )
public interface LambdaFun { Integer getSum(int x, int y); }
-
使用
LambdaFun fn = (int x, int y) -> { return x + y; }; System.out.println(fn.getSum(10, 20));
-
6.3.3 省略规则
-
参数类型可以省略
LambdaFun fn = (x,y) -> { return x + y; };
-
方法体只有一句的时候大括号、return、唯一一句的分号可以省略
LambdaFun fn = (x, y) -> x + y;
-
只有一个参数的时候可以省略小括号
LambdaFun fn = x -> x;
-
如果记不住 那就记住 alt+enter
6.3.4 使用LambdaQueryWrapper
@GetMapping("/conditions/lambda")
public List<Student> queryByConditionsLambda(Student student){
LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
// 添加年龄的判断,第一个参数为true的时候才会拼接到字符串中,第二个参数是数据库的字段名,第三个参数是值
wrapper.eq(student.getAge() != null,Student::getAge, student.getAge());
// 添加姓名的模糊查询
wrapper.like(StringUtils.isNotEmpty(student.getName()),student1 -> student1.getName(), student.getName());
return service.list(wrapper);
}
7、分页查询
7.1 新建配置类
@Configuration
public class MyBatisPlusConfig {
// MybatisPlus的配置
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 在拦截器中加入了一个分页的拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
7.2 使用page方法
public List<Student> queryAll(){
// 创建一个Page对象
Page<Student> studentPage = new Page<>(1,5);
// 传入刚刚的Page对象进行查询,得到一个新的Page对象
Page<Student> page = service.page(studentPage);
// 获取到Page的记录返回,同时也可以获取到总数等
return page.getRecords();
}
8、小结
本章节中我们学习了Mybatis-Plus的概念、和Myabtis的区别、基本使用、条件查询等功能,初步了解了Mybatis-Plus的使用,提高我们操作数据库的效率。
下一节中我们将会继续学期Mybatis-Plus的其他用法,提高自己对Mybatis-Plus的使用熟练度。