目录
什么是MyBatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
mybatis plus 官网:https://baomidou.com/
使用前可以安装 MybatisX 插件提升开发效率。
MyBatis-Plus基本特性
自动配置
- MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对mybatis-plus的定制
- SqlSessionFactory 自动配置好。底层是容器中默认的数据源
- mapperLocations 自动配置好的。有默认值。
classpath*:/mapper/**/*.xml
;任意包的类路径下的所有* mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下 - 容器中也自动配置好了 SqlSessionTemplate
- @Mapper 标注的接口也会被自动扫描;建议直接
@MapperScan("cn.kt.xxxx.mapper")
批量扫描就行
优点:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
缺点:
- 只支持单表的CRUD构造器操作,如果业务复杂,涉及多表联合查询,级联等操作,还是需要像MyBatis一样手动配置。
- 只能算是Mybatis的升级版,很多其他工具的整合都是默认使用Spring Data JPA,看公司的使用习惯吧。
框架结构
整合和使用MyBatis-Plus
1. 建测试表
现有在 ceshi 的数据库下建一张 User 表,其表结构如下:
数据库脚本如下:
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
`age` int(11) NULL DEFAULT NULL COMMENT '年龄',
`email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`createtime` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP,
`updatetime` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'Jone', 18, 'test1@baomidou.com', '2021-08-17 09:45:31', '2021-08-17 09:45:31');
INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@baomidou.com', '2021-08-17 09:45:31', '2021-08-17 09:45:31');
INSERT INTO `user` VALUES (3, 'Tom', 28, 'test3@baomidou.com', '2021-08-17 09:45:31', '2021-08-17 09:45:31');
INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@baomidou.com', '2021-08-17 09:45:31', '2021-08-17 09:45:31');
INSERT INTO `user` VALUES (5, 'Billie', 24, 'test5@qq.com', '2021-08-17 09:45:31', '2021-08-17 10:32:03');
INSERT INTO `user` VALUES (13, '如我西沉', 36, '2222222@qq.com', '2021-08-17 10:04:54', '2021-08-17 11:24:48');
2. 新建springboot项目、导入相关依赖
测试项目目录结构如下
相关依赖
<?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>
<groupId>cn.kt</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>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mybatis-plus-->
<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>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
说明:我们使用mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus! 版本的差异!
3. 配置文件application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/ceshi?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
#配置日志,我们所用的sql现在是不可见的,我们希望知道他是怎么执行的,所以我们必须要查看日志!
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4. 配置分页插件和自动化填充时间
MybatisPlusInterceptor:配置mybatis-plus的分页插件
新建 /config/MyBatisConfig.java
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author tao
* @date 2021-08-16 23:11
* 概要: 配置mybatis-plus的分页插件
*/
@Configuration
public class MyBatisConfig {
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
//这是分页拦截器
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setOverflow(true);
paginationInnerInterceptor.setMaxLimit(500L);
mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);
return mybatisPlusInterceptor;
}
}
MetaObjectHandler:自动化填充创建时间、修改时间
创建时间、修改时间!这些个操作一般都是自动化完成,我们不希望手动更新!
方式一:数据库级别 (工作中不允许)
在表中新增字段 create_time 、update_time,设为默认CURRENT_TIMESIAMP
方式二:代码级别
在表中新增字段 createtime 、updatetime:
新建 /config/MyMetaObjectHandler.java
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* Created by tao.
* Date: 2021/8/17 9:53
* 描述: 自动化填充创建时间、修改时间
*/
@Configuration
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createtime",new Date(),metaObject);
this.setFieldValByName("updatetime",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updatetime",new Date(),metaObject);
}
}
5. 新建实体类
新建 /domain/User.java
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* @author tao
* @date 2021-08-16 22:03
* 概要:
*/
@Data
@TableName("user")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
//创建时间,插入数据时操作
@TableField(fill = FieldFill.INSERT)
private Date createtime;
//更新时间,插入和更新是操作
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updatetime;
public User(String name, Integer age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public User() {
}
}
说明:
主键生成策略:数据库插入的id为全局默认的id(ID_WORKER),我们需要配置主键自增,在实体类字段上添加注解: @TableId(type =IdType.AUTO)
,数据库字段一定要是自增的。
AUTO(0), //数据可id自增
NONE(1), //未设置主键
INPUT(2), //手动输入
ID_WORKER(3), //默认的全局唯一id
UUID(4), //全局唯一id uuid
ID_WORKER_STR(5); // ID_WORKEK 字符串表示法
如果主键没有设置Id自增,则mybatis-plus默认使用雪花算法生成Id.
雪花算法:
SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的。
这 64 个 bit 中,其中 1 个 bit 是不用的,然后用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号。
6. 编写dao层
使用mybatis-plus后,只需要编写一个接口继承 BaseMapper,就可以实现基本的CRUD操作。
新建 /mapper/UserMapper.java
import cn.kt.mybatisplus.domain.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author tao
* @date 2021-08-16 22:04
* 概要:
*/
public interface UserMapper extends BaseMapper<User> {
}
7. 编写测试方法
主要测试
* 根据Id查询
* 查询全部
* 增加一条数据
* 修改一条数据
* 根据Id删除一条数据
* 分页查询
* 根据某个字段条件查询
* 查询name不为null的用户,并且邮箱不为null的永不,年龄大于等于35的用户
* 查询年龄在10~30岁之间的用户
* 模糊查询
* 子sql查询
* 询排序:通过id进行排序
测试代码如下
import cn.kt.mybatisplus.domain.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
/**
* @author tao
* @date 2021-08-16 22:14
* 概要:
*/
@SpringBootTest
class UserMapperTest {
@Autowired
UserMapper userMapper;
//根据Id查询
@Test
void findById() {
User user = userMapper.selectById(1L);
System.out.println(user.getName() + "---" + user.getEmail());
}
//查询全部
@Test
void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
//增加一条数据
@Test
void testSave() {
User nick = new User("Nick", 21, "12055@qq.com");
userMapper.insert(nick);
}
//修改一条数据
@Test
void testUpdate() {
User nick = new User("如我西沉", 21, "12055@qq.com");
nick.setId(13L);
userMapper.updateById(nick);
}
//根据Id删除一条数据
@Test
void testDelete() {
userMapper.deleteById(1L);
}
//分页查询
@Test
void findByPage() {
Page<User> page = new Page<>(1, 3);
Page<User> userListPage = userMapper.selectPage(page, null);
System.out.println("当前页号:" + userListPage.getCurrent());
System.out.println("每页多少条数据:" + userListPage.getSize());
System.out.println("有多少页:" + userListPage.getPages());
System.out.println("有多少条记录:" + userListPage.getTotal());
System.out.println("=========当前页的数据=========");
userListPage.getRecords().forEach(System.out::println);
}
//根据某个字段条件查询
@Test
void findByName() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "Jack");
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
// 查询name不为null的用户,并且邮箱不为null的永不,年龄大于等于35的用户
@Test
void findByAgeLessThan() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name");
wrapper.isNotNull("email");
wrapper.ge("age", 35);
userMapper.selectList(wrapper).forEach(System.out::println);
}
// 查询年龄在10~30岁之间的用户
@Test
void findByAgeBetween() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30);
Integer count = userMapper.selectCount(wrapper);//查询结果数
System.out.println(count);
}
//模糊查询
@Test
void findByLink() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.notLike("name", "ll");//相当于NOT LIKE '%Z%'
wrapper.likeLeft("email", "@qq.com");//相当于LIKE '%@qq.com'
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);//查询结果数
maps.forEach(System.out::println);
}
//子查询
@Test
void findByWrapperId() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.inSql("id", "select id from user where id<4");
List<User> users = userMapper.selectList(wrapper);
System.out.println("===================================");
users.forEach(System.out::println);
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
System.out.println("===================================");
}
//查询排序:通过id进行排序
@Test
void findByIdOrderByIdAsc() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("age");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
}
全部测试成功,测试结果如下:
8. Service层的简化操作
mybatis-plus在service层也做了相应的简化操作:自动化配置单表的CRUD,在service层也可以不写代码,但是如果需要逻辑操作,则需要自己重写接口。
- UserService接口继承 mybatis-plus的IService接口
- 在UserServiceImpl实现类中继承ServiceImpl,并且配置相应的mapper和实体类则可以让Service也具有相应的CRUD功能。
UserService.java
import cn.kt.mybatisplus.domain.User;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* Service 的CRUD也不用写了
*/
public interface UserService extends IService<User> {
}
UserServiceImpl.java
import cn.kt.mybatisplus.domain.User;
import cn.kt.mybatisplus.mapper.UserMapper;
import cn.kt.mybatisplus.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* @author tao
* @date 2021-08-16 22:33
* 概要:
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
测试代码如下
import cn.kt.mybatisplus.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* Created by tao.
* Date: 2021/8/17 11:10
* 描述:
*/
@SpringBootTest
class UserServiceTest {
@Autowired
UserService userService;
@Test
void findAll() {
List<User> list = userService.list();
list.forEach(System.out::println);
}
@Test
void findOne() {
User user = userService.getById(13L);
System.out.println(user);
}
@Test
void save() {
User nick = new User("Nick", 33, "111111@qq.com");
userService.save(nick);
}
@Test
void update() {
User user = userService.getById(13L);
user.setEmail("2222222@qq.com");
userService.updateById(user);
}
@Test
void delete() {
boolean isDelete = userService.removeById(14L);
System.out.println(isDelete);
}
}
总结
mybatis plus极大的增强了开发效率,在某些复杂的SQL操作中,也可以和mybatis一样,灵活的使用配置xml或者使用注解。具体详情可以参考我之前的文章:MyBatis多表查询详解