什么是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 ```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多表查询详解