MyBatisPlus
MyBatisPlus也是一款数据持久持久层的框架,他是对MyBatis的一个增强。对于一个项目MyBatisPlus可以做到无侵入的引入到项目中进行使用,并不影响MyBatis的使用。其支持一下特性:
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
-
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
-
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码
-
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
-
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
-
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
基本使用
引入Maven依赖
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
在启动类上添加@MapperScan
扫描注解指定mapper目录
@MapperScan("com.hhy.mp.mapper")
在Mapper接口拓展MyBatisPlus提供的 BaseMapper接口并指定对应的泛型
public interface UserMapper extends BaseMapper<User> {
}
直接注入Mapper接口即可使用对应的CRUD方法
注意事项&常用注解
MyBatisPlus通过扫描实体类,并通过反射获取实体类信息作为数据库表信息。
MyBatisPlus使用时需要遵守的约定:
- 类目驼峰转下划线作为表名
- 名为id的字段作为主键
- 变量名驼峰转下划线作为表的字段名
如果我们没有遵守约定的话,就需要通过注解来指定对应的名称。
MybatisPlus中比较常用的几个注解如下:
•@TableName:用来指定表名
•@TableId:用来指定表中的主键字段信息
•@TableField:用来指定表中的普通字段信息
IdType枚举:
- AUTO:数据库自增长
- INPUT:通过set方法自行输入
- ASSIGN_ID:分配 ID,接口IdentifierGenerator的方法nextId来生成id,默认实现类为DefaultIdentifierGenerator雪花算法
使用@TableField的常见场景:
- 成员变量名与数据库字段名不一致
- 成员变量名以is开头,且是布尔值,MyBaits会将is去掉当成字段
- 成员变量名与数据库关键字冲突
- 成员变量不是数据库字段
@TableName("tb_user")
public class User {
@TableId(value="id",type=IdType.AUTO)
private Long id;
@TableField("username")//成员变量名与数据库字段名不一致
private String name;
@TableField("is_married")//成员变量名以is开头,且是布尔值,MyBaits会将is去掉当成字段
private Boolean isMarried;
@TableField("`order`")//成员变量名与数据库关键字冲突
private Integer order;
@TableField(exist = false)//成员变量不是数据库字段
private String address;
}
配置文件
MyBatisPlus的配置项继承了MyBatis原生配置和一些自己特有的配置。例如:
注解的优先级时高于配置文件的
mybatis-plus:
type-aliases-package: com.hhy.mp.domain.po # 别名扫描包
mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值
configuration:
map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射
cache-enabled: false # 是否开启二级缓存
global-config:
db-config:
id-type: assign_id # id为雪花算法生成
update-strategy: not_null # 更新策略:只更新非空字段
核心功能
条件构造器
MyBatisPlus页提供了复杂SQL的构造,通过条件构造器就可以构造复杂SQL。
基本使用
进行复杂一点的查询和修改
@Test
void select() {
// 查询名字里包含o并且余额大于等于800的用户
QueryWrapper<User> queryWrapper = new QueryWrapper<User>().select("id", "username", "info", "balance")
.like("username", "o").ge("balance", 800);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
// 更新的值
User user = new User();
user.setBalance(2000);
// 更新条件
UpdateWrapper<User> wrapper = new UpdateWrapper<User>().eq("username","jack");
userMapper.update(user,wrapper);
// 需求:更新id为1,2,4的用户的余额,扣200
UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>().setSql("balance = balance - 200").in("id", 1, 2, 4);
userMapper.update(null,updateWrapper);
}
建议使用Lambda的这种写法,配和方法引用来使用
这种方式是通过反射来获取类型,这种方式没有使用硬编码
@Test
void select() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>().select(User::getId, User::getUsername, User::getInfo,User::getBalance)
.like(User::getUsername, "o").ge(User::getBalance, 800);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
条件构造器的用法:
- QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
- 0UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
- 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码
自定义SQL
我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。
需求:将id在指定范围的用户(例如1、2、4 )的余额扣减指定值,如果不想在业务层编写SQL代码的话,这个需求用自定义SQL实现
void select() {
List<Long> idList = new ArrayList<Long>(){
{
add(1L);
add(2L);
add(4L);
}
};
int amount = 200;
// 1.构造条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User::getId, idList);
// 2.自定义SQL方法调用
userMapper.updateBlanceByIds(wrapper, amount);
}
// 在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew
void updateBlanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper,@Param("amount") int amount);
<update id="updateBlanceByIds">
UPDATE user SET balance = balance - #{amount} ${ew.customSqlSegment}
</update>
最后生成的SQL:UPDATE user SET balance = balance - ? WHERE (id IN (?,?,?))
IService接口
条件构造器Wrapper
是为了简化Mapper的SQL编写,而IService
接口是为了简化Service层的代码编写。通用 Service CRUD 封装IService
接口,进一步封装 CRUD 采用 get 查询单行
remove 删除
list 查询集合
page 分页
前缀命名方式区分 Mapper
层避免混淆。
Iservice
是MyBatisPlus提供的接口而ServiceImpl是该接口的实现类,提供了大量的CRUD方法
- UserService是我们自己定义的接口,UserServiceImpl是我们自己定义的实现类
- 当UserService拓展了IService后,UserServiceImpl就要实现里面的大量方法
- 此时就可以继承MyBatisPlus提供的ServiceImpl类就需要重写大量方法了,因为ServiceImpl已经帮我们实现了对应方法
IService的使用
先在自己定义的业务接口拓展MyBatisPlus提供的IService并指定要操作的数据泛型
// 拓展IService
public interface UserService extends IService<User> {
}
接着在对于业务实现类继承MyBatisPlus提供的ServiceImpl类并指定泛型参数:
- 参数1为要调用的Mapper
- 参数2为Mapper操作的数据对象
再实现对应的业务接口
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
}
Iservice提供了对应的lambdaUpdate()
和lambdaQuery()
来构造复杂更新和查询
IService批量新增
我们知道批量新增数据和一条一条插入数据的性能差距是非常大的,因为每一次insert都都是一次网络请求。
假设要插入10w条数据,就可以使用MyBitsPlus的批量插入,因为MyBitsPlus的批量新增,基于预编译的批处理,性能不错
10w条数据每次插入1000条数据,就会把1000条SQL拼接成一条insert into user ... values (...),(...),(...)
,效率就会非常高,如果接直接插入10w条数据的话会占用内存空间。通过saveBatch()
方法就能实现批量插入。
需要注意的是:1000条插入数据是批量提交,当MySQL依旧是一条一条执行的。如果需要批量执行,则需要开启rewriteBatchedStatements=true参数