MyBatisPlus
Mybatis-Plus是一个MyBatis的增强工具,在mybatis的基础上只做增强不做改变
简化开发,提高效率
MP的特性
无侵入
只做增强不做改变
损耗小
启动时自动注入基本CRUD,性能几乎无循环
强大的CRUD操作
内置通用Mapper和Service实现大部分单表操作
支持Lambda形式调用
通过Lambda表达式编写各类的查询条件
支持主键自动生成
支持4种主键策略(自增,雪花算法,UUID等)
支持ActiveRecord模式
实体类继承Model即可进行CRUD操作
支持自定义全局通用操作
全局通用方法注入
内置代码生成器
可快速生成各层代码
内置分页插件
基于MyBatis进行物理分页
分页插件支持多种数据库
内置性能分析插件
内置全局拦截插件
MyBatisPlus配置
依赖注入
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version>
</dependency>
配置Datasource
//控制台打印sql配置
#mybatis-plus配置控制台打印完整带参数SQL语句
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
定义实体类
package com.cfjg.pojo;import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 实体类基于注解与表进行映射*/
@Data
@Builder
// 指定表名
@TableName("tb_user")
@NoArgsConstructor
@AllArgsConstructor
public class User {//主键字段注解//设置主键自动生成方法//AUTO(0),increment自增//NONE(1),跟随全局//INPUT(2),自定义主键,自己输入//ASSIGN_ID(3),雪花算法//雪花算法生成的id为19为数字,由时间戳/数据中心/机器标识/序列号四个部分组成//ASSIGN_UUID(4);UUID@TableId(value = "id",type = IdType.AUTO)private Long id;private String userName;private String password;private String name;private Integer age;private String email;//忽略该属性和数据库表中的映射关系@TableField(exist = false)private String ignore
}
MyBatisPlus实现数据库操作
在Mapper层继承BaseMapper
//泛型内写表对应的实体类类型
UserMapper extends BaseMapper<User>
普通单表操作都在接口中进行定义
直接调用接口方法即可
注:MP的更新操作不会将属性的空值覆盖原本的值
@Testpublic void testUserInsert(){User user = User.builder().age(19).build();userMapper.insert(user);}
分页查询
需要先配置分页拦截器
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false// paginationInterceptor.setOverflow(false);// 设置最大单页限制数量,-1不受限制paginationInterceptor.setMaxLimit(-1L);interceptor.addInnerInterceptor(paginationInterceptor);return interceptor;}
}
进行分页查询
@Testpublic void testSelectPage() {//创建分页对象,查询完成数据会回填到page对象中Page<User> page = new Page<>(1,2);//构建条件对象QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("age","19");userMapper.selectPage(page, wrapper);System.out.println(page.getRecords());System.out.println(page.getTotal());System.out.println(page.getPages());}
条件查询
使用wrapper接口实现条件查询和更新
//查询接口
QueryWrapper
//更新接口
UpdateWrapper
QueryWrapper常用API
//equal
eq( ) : 等于 =
//not equal
ne( ) : 不等于 <> 或者 !=
//greater than
gt( ) : 大于 >
//greater equal
ge( ) : 大于等于 >=
//less than
lt( ) : 小于 <
//less equal
le( ) : 小于等于 <=
or():或
between ( ) : BETWEEN 值1 AND 值2
notBetween ( ) : NOT BETWEEN 值1 AND 值2
in( ) : in
notIn( ) :not in
like(): like模糊查询%xxx%
likeLeft():左侧模糊查询%xxx
likeRight():右侧模糊查询xxx%
orderByAsc()
orderByDesc()
条件查询操作
@Testpublic void testCondition(){QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("age",19).or().lt("age",30);List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}
限定字段查询
通过wrapper限定select查询的字段
wrapper.eq("age",19).or().lt("age",30).select("age");
LambdaQueryWrapper查询
避免使用普通wrapper时出现的硬编码列名问题,通过传入对应属性值的get方法解析出这个属性对应的字段
@Testpublic void testLambdaSelect(){LambdaQueryWrapper<User> eq = Wrappers.<User>lambdaQuery().eq(User::getAge, 19);userMapper.selectList(eq);Wrappers.<User>lambdaUpdate().eq(User::getAge,19);}
LambdaQueryWrapper删除
@Testpublic void testDelete(){LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery().gt(User::getAge, 19);userMapper.delete(wrapper);}
LambdaQueryWrapper更新
@Testpublic void testUpdate(){LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery().eq(User::getUserName,null);User user = new User();user.setName("a");user.setAge(11);user.setUserName("b");userMapper.update(user,wrapper);}
定义查询接口实现分页查询
对自定义SQL语句进行分页查询
@Testpublic void sqlTest(){Page<User> page = new Page<>(1,2);userMapper.findGtIdByPage(page,1);page.getRecords().forEach(System.out::println);}
mybatisplus对应的xml文件在resource下的/mapper/**/*.xml读取
sql:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper"><select id="findGtIdByPage" resultType="com.itheima.pojo.User">select * from tb_user where id > #{id}</select>
</mapper>
通过${ew.}实现条件构造器复用
Wrapper ew//传入构造器替换select后的内容
${ew.sqlSelect}//传入构造器替换sql中的条件,放在where后面
{ew.customSqlSegment}
MP实现Service封装
继承公共接口
//service接口继承Iservice接口
public interface UserService extends IService<User>//service实现类继承ServiceImpl接口
//泛型内写Mapper对象和实体类对象
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
MP封装Service实现CRUD操作
大体和持久层一致
先构造条件构造器
再传入条件构造器实现条件查询
@Testpublic void UserTest(){LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery().eq(User::getUserName,"test");System.out.println(userService.list(wrapper));}
MP代码生成器
自动生成控制层业务层mapper层和xml代码
只需指定数据库和生成的表名即可
导入依赖
<!--mp 代码生成器-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.5</version>
</dependency>
<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.31</version>
</dependency>
代码生成器
package com.itheima;import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;import java.sql.Types;
import java.util.Collections;public class CodeGenerator {public static void main(String[] args) {String url = "jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC";String username = "root";String password = "root";FastAutoGenerator.create(url, username, password).globalConfig(builder -> {builder.author("itheima") // 设置作者.enableSwagger() // 开启 swagger 模式.outputDir("F:\\code\\mp"); // 指定输出目录}).dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {int typeCode = metaInfo.getJdbcType().TYPE_CODE;if (typeCode == Types.SMALLINT) {// 自定义类型转换return DbColumnType.INTEGER;}return typeRegistry.getColumnType(metaInfo);})).packageConfig(builder -> {builder.parent("com.itheima") // 设置父包名.moduleName("user") // 设置父包模块名.pathInfo(Collections.singletonMap(OutputFile.xml, "F:\\code\\mp")); // 设置mapperXml生成路径}).strategyConfig(builder -> { //策略配置builder.addInclude("tb_user") // 设置需要生成的表名.addTablePrefix("tb_") // 设置过滤表前缀.entityBuilder() //设置实体构建器,设置属性.enableFileOverride() //文件覆盖.enableLombok() //开启lombok.controllerBuilder().enableRestStyle() //启用RestController.enableFileOverride() //文件覆盖.mapperBuilder().enableBaseResultMap() //开启生成 resultMap 结果映射.enableFileOverride() //文件覆盖;}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板.execute();}
}
MybatisPlus逻辑删除
即在数据库中添加一个is_delete字段用来表示数据是否被删除
而非真的将数据删除,只是对数据进行标注
用这种方法删除的数据是可逆的,可以恢复
mybatisplus配置逻辑删除
mybatis-plus:global-config:db-config:logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
实体类字段加@TableLogic注释
@TableLogic
private Integer is_deleted;
开启逻辑删除功能后,mp在删除查询和更新时会自动加上逻辑删除字段为未删除的条件
MybatisPlus乐观锁
相对于悲观锁而言,乐观锁假设数据在一般情况下不会发生冲突
只有在数据进行提交和更新时才会正式对数据的冲突与否进行检测
如果冲突,则返回错误的信息让用户决定后续操作
相比于悲观锁,并发下的程序吞吐量更大
乐观锁的实现方式
版本号控制
通过数据量增加一个version版本字段,判断每次读取的version版本和数据库记录的是否一致,如果不一致则过期
//通过对实体类字段添加@version注解来标记锁
@version
private Integer version
注册配置类
需要先注册乐观锁插件(乐观锁拦截器)
//扫描我们的repository文件夹
@MapperScan("com.trainingl.repository")
@EnableTransactionManagement
@Configuration //配置类
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();//注册乐观锁插件mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mybatisPlusInterceptor;}}
MybatisPlus自动填充
对于创建时间create_time,更新时间update_time这些每次对数据修改时都要设置的字段,如果在代码中进行手动设置会显得冗余且容易出错
mp提供了自动填充的功能
实现过程
通过给实体类添加@TableField注解来进行指定填充的时间
FieldFill字段枚举类有四种自动填充处理策略,分别是Default (默认不处理)、Insert (插入时填充)、Update (更新时填充)、Insert_Update (插入和更新时填充)
@TableField(fill = FieldFill.INSERT)private LocalDateTime creatTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;
再通过实现MetaObjectHandler接口对填充策略进行设计
/*** mybatis-plus自动填充策略设置*/
@Slf4j
@Component
public class DatetimeMetaObjectHandler implements MetaObjectHandler {//进行插入时填充策略@Overridepublic void insertFill(MetaObject metaObject) {log.info("mybatis-plus 开始在你插入的时候 字段填充字段......");this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);}//进行修改操作时填充策略@Overridepublic void updateFill(MetaObject metaObject) {log.info("mybatis-plus 开始在你修改的时候 字段填充字段......");this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);}
}
MybatisPlus通用枚举
实现数据库存入时指定值保存,读取时指定值展示
通过继承IEnum,@EnumValue实现
读取数据时可以通过使用@JsonValue执行显示的值
@AllArgsConstructor
public enum SexEnum implements IEnum<Integer> {boy(0, "男孩"),girl(1, "女孩");private final Integer code;// 序列化枚举值为 接口出参;接口入参(RequestBody),反序列化为枚举值@JsonValueprivate final String name;@Overridepublic Integer getValue() {return code;}
}
或
@AllArgsConstructor
public enum SexEnum {boy(0, "男孩"),girl(1, "女孩");@EnumValueprivate final Integer code;@JsonValueprivate final String name;}
MybatisPlus拦截器插件
防全表更新和删除插件
全表更新时会抛出异常
@Configuration
public class MybatisPlusConfig {//拦截器@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//注册分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//注册防全表更新与删除插件interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());return interceptor;}
}
MybatisPlus流式查询
流式查询:查询成功后不是返回一个集合而是返回一个迭代器,每次从迭代器取出一条结果进行查询
可以有效的降低内存使用
执行流式查询时,框架不负责对数据库连接进行关闭,需要在取完数据之后进行关闭
Mybatis的流式查询
提供了一个Cursor接口用于流式查询
继承了 java.io.Closeable 和 java.lang.Iterable 接口
所以流式查询的过程是可关闭和可遍历的
提供了三个方法
//用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据
isOpen()
//用于判断查询结果是否全部取完
isConsumed()
//返回已经获取了多少条数据
getCurrentIndex()
MyBatisPlus实现流式查询
需要自定义接口实现