一、简介
简单来说,mybatis-plus并没有对mybatis功能上进行增强,只是简化了我们的操作,提高开发效率。
二、特性(来自官方)
1.无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
2.损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作,BaseMapper
3.强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求,简单的CRUD操作不用自己编写。
4.支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
5.支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
6.支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
7.支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
8.内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(自动生成代码)
9.内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
10.分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
11.内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
12.内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
三、SpringBoot搭建Mybatis-plus环境
1.创建maven项目
2.pom.xml(导入相关依赖)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zh.mp</groupId>
<artifactId>mybatisplus</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mybatisplus</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.application.yml
#mysql数据库连接
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
#mybatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4.数据库表结构
5.创建User实体
@Data
@TableName("user") //指定实体对应的表
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private Date createTime;
private Date updateTime;
private Integer version;
private Integer deleted;
}
6.创建UserMapper
public interface UserMapper extends BaseMapper<User> {
// UserMapper创建完毕,对user表的基本操作已经可以进行了,可以看到我们不需要再写UserMapper.xml
}
基本环境准备完成!!!下来就玩玩mybatis-plus吧。
四、完成CRUD
1.查找所有user
@Autowired
private UserMapper userMapper;
@Test
public void findAll() {
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
2.根据id查找单个user
@Test
public void findUserById() {
User user = userMapper.selectById(1);
System.out.println(user);
}
3.新增user
@Test
public void saveUser() {
User user = new User();
user.setName("ceshi");
user.setAge(18);
user.setEmail("118027@qq.com");
userMapper.insert(user);
}
可以看到数据库中成功新增了一条记录!!!
但是,值得注意的是,博主在新增时,是没有指定id值的并且数据库id也没有设置自增
那么这个id怎么来的呢?相信聪明的你已经猜到了,没错,来源于mybatis-plus的主键生成策略
4.主键生成策略
public enum IdType {
/**
* 数据库ID自增
* <p>该类型请确保数据库设置了 ID自增 否则无效</p>
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
大家可以在依赖中找到这个枚举类查看
在上面新增案例中,其实默认使用ASSIGN_ID(3),相当于下面代码
5.根据id修改user
修改age一个字段
@Test
public void updateUserById() {
User user = new User();
user.setId(1604093390215753729L);
user.setAge(20);
userMapper.updateById(user);
}
修改age和name两个字段
@Test
public void updateUserById() {
User user = new User();
user.setId(1604093390215753729L);
user.setAge(22);
user.setName("test");
userMapper.updateById(user);
}
经过对比两条sql,可以发现set的字段数量发生了变化,可以推测出,update SQL是mybatis-plus动态拼接生成的
6.根据id删除user
@Test
public void deleteById() {
userMapper.deleteById(1604103038335488001L);
}
五、根据条件查询
@Test
public void findByWrapper() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("age",16);
// queryWrapper.ne("age",16); //age != 16
// queryWrapper.gt("age",16); //age > 16
// queryWrapper.ge("age",16); //age >= 16
// queryWrapper.lt("age",16); //age < 16
// queryWrapper.le("age",16); //age <= 16
// //age在16到28之间包含16,28,相似的还有notBetween
// queryWrapper.between("age",16,28);
// queryWrapper.like("name","ll"); //%ll%
// queryWrapper.likeLeft("name","ll"); //%ll 还有 likeRight ll%
queryWrapper.orderByAsc("age"); // 按age排序
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
以上就是比较常见的条件,遇见不会的可以再查
六、逻辑删除
逻辑删除就是在表中增加一个字段代表该用户是否已删除。比如,我们的表中的deleted字段,0代表未删除,1代表已删除
1.在实体类中的deleted添加注解 @TableLogic
@TableLogic
private Integer deleted;
2.添加逻辑删除插件
@Configuration
public class MyBatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
3.查找所有user
数据库有9条数据,但是deleted为0的只有7条,deleted为1的代表已删除
可以看到SQL自动的添加了where deleted = 0
4.删除一个user
@Test
public void delectedByLogic(){
int i = userMapper.deleteById(1577504228629024769L);
System.out.println(i);
}
发现此时在删除的话,只是修改了deleted的值为1
七 、分页查询
1.添加分页的插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
2.分页查询
@Test
public void findByPage() {
Page<User> page = new Page<>(1,3);
Page<User> userPage = userMapper.selectPage(page, null);
List<User> records = userPage.getRecords();//每页的数据
long current = userPage.getCurrent();//当前页
long size = userPage.getSize();//每页显示的数据大小
long total = userPage.getTotal();// 该表的总数据
long pages = userPage.getPages();// 总页数
System.out.println(records);
System.out.println("当前页:"+current+"size:"+size+"total:"+total+"pages:"+pages);
}
八、乐观锁
悲观锁:每次都认为,我在操作数据时,被人也在操作。因此,我操作数据时,先加一把锁,不让别人操作。
乐观锁:只有在我真正要操作数据时,才判断是否有人操作过数据,是,我的操作失败!否,则我的操作成功!
实现原理:在表中增加一个version字段,我每次操作数据时,先查询该字段的值,然后,我操作数据时会再次比较此时version的值和之前查到的version值是否相等。相等,则操作成功,否则,操作失败!
1.在实体类version属性上添加注解@Version
@Version
private Integer version;
2.操作数据(成功案例)
@Test
public void optimisticLockerTest() {
//1、查询用户信息
User user = userMapper.selectById(1L);
//2、修改用户信息
user.setEmail("123@qq.com");
user.setName("小垃圾");
//3、更新操作
userMapper.updateById(user);
}
2.操作数据(失败案例)
@Test
public void optimisticLockerFailTest() {
//线程1
User user = userMapper.selectById(1L);
user.setEmail("123jdw@qq.com");
user.setName("帅小伙111");
//线程2插队
User user2 = userMapper.selectById(1L);
user2.setEmail("321jdw@qq.com");
user2.setName("帅小伙222");
userMapper.updateById(user2);
userMapper.updateById(user);
}
线程1先查到id为1的version值为2
线程2再查到id为1的version值为2
线程2比较version值就是上次查到值2,更新成功并且将version值更新为3
线程1比较上次的version值,此时发现version已将为3了(线程1修改的),更新失败!!!
所以,线程2更新成功,线程1更新失败
九、自动填充
自动填充createTime和updateTime的值
1.在实体类上属性加如下注解
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
2.自定义实现类MyMetaObjectHandler
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
this.strictInsertFill(metaObject, "version", Integer.class, 0);
this.strictInsertFill(metaObject, "deleted", Integer.class, 0);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
}
}
3.新增
@Test
public void saveUser() {
User user = new User();
user.setName("ceshi");
user.setAge(18);
user.setEmail("118027@qq.com");
userMapper.insert(user);
}
之前新增,后面四个字段都为null,使用自动填充让它们新增时有默认值。
4.修改
@Test
public void updateUserById() {
User user = new User();
user.setId(1604142614580080641L);
user.setAge(22);
user.setName("test");
userMapper.updateById(user);
}
修改后,updateTime字段也自动的跟新