MyBatis Plus
入门
概述
MyBatisPlus ( 简称MP)是基于MyBatis框架基础_上开发的增强型工具,旨在简化开发、提高效率
官网: https:/ /mybatis.plus/ https:/ /mp. baomidou. com/
特性
- 无侵入:只做增强不做改变,不会对现有工程产生影响
- 强大的CRUD 操作:内置通用Mapper, 少量配置即可实现单表CRUD操作
- 支持Lambda:编写查询条件无需担心字段写错
- 支持主键自动生成
- 内置分页插件
案例
基于SpringBoot使用MyBatisPlus
核心步骤:
- 配置mapper文件(数据层接口映射配置)
- 配置数据源信息(jdbc参数)
-
起步依赖:MySQL Driver
-
pom中添加mybatisplus依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency>
还有druid,版本是1.1.16
-
写application.yml
-
做数据层对应的功能(domain、dao)
-
写测试类
标准数据层开发
标准数据层CRUD功能
- 修改操作:
user.setId(1L)
后面要加L:来标识这个数字是long
类型的(默认会解释为int
类型),而long和id的bigint可以互相兼容(都是64位,而int是32位) - 分页查询:需要先配置mybatis-plus的拦截器(MpConfig)然后在测试方法中根据需求编写
lombok
带有能快速开发实体类的注解,需要现在pom中添加
- @EqualsAndHashCode:生成对象的哈希码,通过哈希码比较两个对象是否相等
- @Data可以替代上面除了构造方法以外的所有方法
配置运行日志
在application.yml中配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
DQL编程控制
条件查询
MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合
Wrapper<T>
是一个查询条件的封装器,用于构建查询条件。可以在 wrapper
中指定查询条件,例如等于、大于、小于等
-
按条件查询:
QueryWrapper qw = new QueryWrapper(); qw.lt("age", 18); List<User> userList = userDao.selectList(qw); System.out.println(userList);
-
lambda格式按条件查询:编译器可以检查字段的类型和是否存在于实体类中,从而提供更好的类型检查。
QueryWrapper qw = new QueryWrapper(); qW.lambda().lt(User::getAge, 18); List<User> userList = userDao.selectList(qw); System.out.println(userList);
-
使用LambdaQueryWrapper(直接使用实体类的属性和方法),代码更加简洁、更具有类型安全性,并且使其对于 IDE 友好
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); lqw.lt("age",65).ge("age" ,18);//链式编程 List<User> userList = userDao. selectList(lqw); System. out. println(userList);
组合查询:
- and:
1qW. lt(User: : getAge, 65). ge(User: :getAge,18);
- or:
lqw. lt(User: :getAge, 65).or(). ge(User: :getAge,18) ;
- and:
查询条件设置
例如:
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<User>();
// 范围查询 lt Le gt ge eq between
qw.between(User::getAge, 19, 30);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
- 范围匹配(>、=、between)
=
:lqw.eq(User::getName, "Jerry").eq(User: :getPassword, "jerry");
<=
:le
<
:lt
>=
:ge
>
:gt
- 范围:
between
- 模糊匹配 (like):
like
:lqw. like(User::getName, "J");|
likeright
:百分号加右边(J%)
- 空判定 (null)
- 包含性匹配 (in)
- 分组 ( group)
- 排序(order)
- …
null值判定
- if语句控制条件追加
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
if (null != userQuery.getAge()) {
lqw.ge(User::getAge, userQuery.getAge());
}
if (null != userQuery.getAge2()) {
lqw.lt(User::getAge, userQuery.getAge2());
}
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 条件参数控制
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.ge(null != userQuery.getAge(), User::getAge, userQuery.getAge());
lqw.lt(null != userQuery.getAge2(), User::getAge, userQuery.getAge2());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 条件参数控制(链式编程)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.ge(null != userQuery.getAge(), User::getAge, userQuery.getAge())
.lt(null != userQuery.getAge2(), User::getAge, userQuery.getAge2());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
投影查询
即设置查询出来的结果长什么样
- 查询结果包含模型类中部分属性
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.select("id", "name", "age", "tel");
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
- 查询结果包含模型类中未定义的属性(使用聚合函数)
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw. select("count(*) as count, tel");
lqw.groupBy("tel");
List<Map<String, object>> userList = userDao. selectMaps(lqw);
System. out . println(userList);
I
- 更复杂的查询条件,使用mybatis写
映射条件兼容性
- 字段名称与表名映射
- 表字段与编码属性设计不同步:@TableField
- 编码中添加了数据库中未定义的属性:@TableField
- 采用默认查询开放了敏感字段的查看权限(如pwd):@TableField
- 表名和编码开发设计不同步:@TableName
数据库操作
增
id(主键)生成策略
主键(id)生成策略控制:@TableId
:在模型类中(如User)用于表示主键的属性定义上方,设置当前类中主键属性的生成策略
public class User {
@TableId(type = IdType.AUTO)
private Long id;
}
常见输出策略
- AUTO(默认值):
- 自动生成主键值,通常是数据库自增长或序列(如MySQL的自增长字段、Oracle的序列)。(需要关闭数据库该字段的自增功能)
- NONE:
- 不进行自动生成,需要手动设置主键值。
- INPUT:
- 需要手动输入主键值,通常用于需要应用程序代码生成主键值的情况。
- UUID:
- 使用UUID(通常是36个字符的字符串)作为主键值,全局唯一。
- ASSIGN_ID:
- 默认使用雪花算法生成id(可兼容数值型和字符串型)
雪花算法:由64位二进制串构成 - 指定一个ID生成器,例如:可以设置一个实现了
IdentifierGenerator
接口的自定义ID生成器。
- 默认使用雪花算法生成id(可兼容数值型和字符串型)
全局设定
做id生成策略的全局设定,每个实体类使用相同的策略
- 位置:application.yml
- 语法:
上面的兼容性配置也可以采用全局配置
删
多记录删除
使用api:userDao.deleteBatchIds(list);
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);
userDao.deleteBatchIds(list);
查询操作大致同上
逻辑删除
- 删除操作业务问题:业务数据从数据库中丢弃(如:若删除离职员工的信息,他手下的业务信息会受到损失)
- 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态(比如不可被select),数据保留在数据库中
添加步骤:
- 在数据库的表中添加逻辑删除字段,默认为0(即没删为0,删了为1)
- 在实体类中添加逻辑删除字段,并添加注解
@TableLogic
,也要设置默认值
全局设定
改
乐观锁
解决并发问题
-
乐观锁:乐观锁假设并发访问的情况下不会发生冲突,因此在读取数据时不会加锁。只有在更新数据时才会检查是否发生了冲突。当一个事务要更新数据时,它会比较当前版本号或时间戳与更新前的版本号或时间戳是否相同。如果相同,说明没有其他事务同时更新了数据,允许更新
适用场景:数据的读取操作远远多于写入操作。冲突的概率较低。 -
悲观锁:悲观锁假设并发访问的情况下会发生冲突,因此在读取数据时就会加锁,以防止其他事务修改数据。悲观锁分为共享锁(Shared Lock)和排他锁(Exclusive Lock)。共享锁允许多个事务同时读取数据,但不允许任何事务修改数据,而排他锁则禁止其他事务读取或修改数据。
适用场景:数据的写入操作较多,冲突概率较高。需要强制确保事务之间的隔离性和一致性。
实际应用:使用版本号version,每次修改都将版本号自增1。
每次更新操作都会比较数据库中存储的版本号与当前事务持有的版本号是否一致。如果不一致,说明其他事务在此之前已经更新了数据,发生了冲突。这时,数据库可以拒绝当前事务的更新操作,以防止数据的不一致性。 这样可以进行冲突检测、顺序性控制和避免无限循环
添加步骤:
- 在数据库表结构中加一个字段标记数据库的操纵者(通常是叫version,int型)
- 在实体类中加入上面的字段,再加注解@Version
- 创建拦截器实现自动加锁
- 使用的时候需要提供version的值
或者先将当前数据查询出来(先查询数据再更新的方式通常会在数据库内部自动管理版本号的变化)
快速开发
代码生成器
提前给出代码模板,自己填充定制需要的参数
- 模板: MyBatisPlus提供
- 数据库相关配置:读取数据库获取信息
- 开发者自定义配置:手工配置
步骤
添加pom坐标
-
代码生成器:
<dependency> <groupId>com. baomidou</ groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency>
-
velocity模板引擎
<dependency> <groupId>org. apache . velocity</ groupId> <artifactId>velocity-engine- core</artifactId> <version>2.3</version> </dependency>
创建Generator类
运行用以生成代码(不用配置模板,直接使用内置模板velocity)
-
创建代码生成器的对象并执行
-
配置数据源
-
设置全局配置
// 设置全局配置 GlobalConfig globalConfig = new GlobalConfig(); //设置了生成代码的输出目录,通常是项目的源代码目录。 globalConfig.setOutputDir(System.getProperty("user.dir") + "/mybatisplus_04_generator/src/main/java"); // 设置生成完毕后是否打开生成代码所在的目录 globalConfig.setOpen(false); // 设置作者 globalConfig.setAuthor("黑马程序员"); // 设置是否覆盖原始生成的文件 globalConfig.setFileOverride(true); // 设置数据层接口名,%s 为占位符,指代模块名称 globalConfig.setMapperName("%sDao"); // 设置Id生成策略 globalConfig.setIdType(IdType.ASSIGN_ID); autoGenerator.setGlobalConfig(globalConfig);
-
设置包名相关配置,mapper.xml可删
//设置包名相关配置 PackageConfig packageInfo = new PackageConfig(); packageInfo.setParent("com.aaa"); // 设置生成的包名,与代码所在位 置不冲突,者 叠加组成完整路径 packageInfo.setEntity(" domain"); //设置实体类包名 packageInfo.setMapper("dao"); //设置数据层包名 autoGenerator.setPackageInfo packageInfo);
-
策略配置
// 策略设置 StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setInclude("tbl_user"); // 设置当前参与生成的表名,参数为可变参数 strategyConfig.setTablePrefix("tbl_"); // 设置数据库表的前缀名称,模块名=数据库表名-前缀名,例如: User = tbl_user - tbl_ strategyConfig.setRestControllerStyle(true); // 设置是否启用 Rest 风格的控制器 strategyConfig.setVersionFieldName("version"); // 设置乐观锁字段名 strategyConfig.setLogicDeleteFieldName("deleted"); // 设置逻辑删除字段名 strategyConfig.setEntityLombokModel(true); // 设置是否启用 Lombok autoGenerator.setStrategy(strategyConfig);