简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
快速上手
首先需要一个springboot 项目
引入MyBatis-Plus 的maven依赖(旧项目注意删除mybatis 相关的依赖):
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
在application.yml 里面配置你的mysql 连接信息和Mybatis-Plus 配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/central_certification?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
mybatis-plus:
//自定义sql 路径
mapper-locations: classpath*:/mapper/**/*.xml
我们在对应数据库新建一个user 表:
CREATE TABLE `central_certification`.`user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '用户表自增id',
`user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '用户名称',
`mail` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '用户邮箱',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '用户密码',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除(0 正常 1 删除)',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_mail_password`(`mail`) USING BTREE COMMENT '邮箱索引'
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
// 插入数据
INSERT INTO `user` VALUES (5, 'wt', '799957684@qq.com', '123456', 0, '2019-12-27 16:37:46', '2019-12-30 12:43:56');
INSERT INTO `user` VALUES (6, '唐江席', '1591226504@qq.com', '123456', 0, '2019-12-27 17:22:23', '2019-12-27 17:22:23');
在bean 目录下新建User 类:
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Builder
@TableName("user")
public class User extends Model<User> {
@TableId(type = IdType.AUTO)
private Long id;
private String userName;
private String password;
private String mail;
private Integer deleted;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
@TableName 用于指定类对应的数据库
@TableId 用于指定自增id 及其id 生产规则:
// 可指定的规则
AUTO(0),
NONE(1),
INPUT(2),
ASSIGN_ID(3),
ASSIGN_UUID(4),
/** @deprecated */
@Deprecated
ID_WORKER(3),
/** @deprecated */
@Deprecated
ID_WORKER_STR(3),
/** @deprecated */
@Deprecated
UUID(4);
Model<T> 类是Mybtais-Plus 的特性支持之一
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
什么意思呢,就是说我们可以直接使用类的对象来实现CRUD 操作,可以看看Model<T> 类中的方法:
这些方法不但方便还做了比较完备的检查,比如insertOrUpdate
// 根据id 来判断是否存在,存在更新,否则新增
public boolean insertOrUpdate() {
return !StringUtils.checkValNull(this.pkVal()) && !Objects.isNull(this.selectById(this.pkVal())) ? this.updateById() : this.insert();
}
我们来测试一下:
@org.junit.Test
public void test2(){
User user = User.builder()
.userName("test")
.mail("testMail")
.password("test")
.build();
user.insert();
System.out.println(userMapper.queryByMail("testMail"));
}
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)
除了继承的方式,我们还可以通过接口来实现,在mapper 里面新建UserMapper:
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.bean.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
@Mapper 可以声明一个mapper ,除此之外可以在启动类指定扫描:
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
BaseMapper 也定义了一些常用的方法可以使用
@SpringBootTest
@RunWith(SpringRunner.class)
public class Test {
@Autowired
private UserMapper userMapper;
@org.junit.Test
public void test(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deleted", 0);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(v -> {
System.out.println(v);
});
}
}
User(id=5, userName=wt, password=123456, mail=799957684@qq.com, deleted=0, createTime=2019-12-27T16:37:46, updateTime=2019-12-30T12:43:56)
User(id=6, userName=唐江席, password=123456, mail=1591226504@qq.com, deleted=0, createTime=2019-12-27T17:22:23, updateTime=2019-12-27T17:22:23)
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)
自定义sql
如果想自己写sql ,就和mybatis 一样
@Mapper
public interface UserMapper extends BaseMapper<User> {
User queryByMail(@Param("mail") String mail);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="queryByMail" resultType="com.example.demo.bean.User">
select *
from user
where mail = #{mail}
</select>
</mapper>
前面我们第一次测试的时候就用了这个方法,这里就不重复展示了。
记得在application.yml 里面配置映射
mybatis-plus:
//自定义sql 路径
mapper-locations: classpath*:/mapper/**/*.xml
批量操作
前面的两种方式我们发现这些方法多是些单体方法,当然还有批量的操作,在service 目录新建一个UserServiceImpl 实现:
package com.example.demo.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.bean.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> {
}
直接测试吧:
package com.example.demo.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.bean.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.stream.Collectors;
@RunWith(SpringRunner.class)
@SpringBootTest
class UserServiceImplTest {
@Autowired
private UserMapper userMapper;
@Autowired
private UserServiceImpl userService;
@Test
public void test() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deleted", 0);
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println("更新前:");
userList.forEach(v -> {
System.out.println(v);
});
// 批量更新userList
userList = userList.stream().map(user -> {
user.setUserName(user.getUserName() + "1");
return user;
}).collect(Collectors.toList());
userService.updateBatchById(userList);
List<User> userUpdateList = userMapper.selectList(queryWrapper);
System.out.println("更新后:");
userUpdateList.forEach(v -> {
System.out.println(v);
});
}
}
更新前:
User(id=5, userName=wt, password=123456, mail=799957684@qq.com, deleted=0, createTime=2019-12-27T16:37:46, updateTime=2019-12-30T12:43:56)
User(id=6, userName=唐江席, password=123456, mail=1591226504@qq.com, deleted=0, createTime=2019-12-27T17:22:23, updateTime=2019-12-27T17:22:23)
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)
更新后:
User(id=5, userName=wt1, password=123456, mail=799957684@qq.com, deleted=0, createTime=2019-12-27T02:37:46, updateTime=2019-12-29T22:43:56)
User(id=6, userName=唐江席1, password=123456, mail=1591226504@qq.com, deleted=0, createTime=2019-12-27T03:22:23, updateTime=2019-12-27T03:22:23)
User(id=7, userName=test1, password=test, mail=testMail, deleted=0, createTime=2020-02-01T06:11:37, updateTime=2020-02-01T06:11:37)
分页操作
使用分页需要先引入配置,不然分页不会生效:
//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
XML 自定义分页
UserMapper.java 方法内容
public interface UserMapper {//可以继承或者不继承BaseMapper
/**
* <p>
* 查询 : 根据state状态查询用户列表,分页显示
* </p>
*
* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象)
* @param state 状态
* @return 分页对象
*/
IPage<User> selectPageVo(Page<?> page, Integer state);
}
UserMapper.xml 等同于编写一个普通 list 查询,mybatis-plus 自动替你分页
<select id="selectPageVo" resultType="com.baomidou.cloud.entity.UserVo">
SELECT id,name FROM user WHERE state=#{state}
</select>
Mybatis-Plus 分页
使用Mybatis-Plus 分页可能会报错提示无法代理
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.demo.service.UserServiceImplTest':
Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userServiceImpl' is expected to be of type
'com.example.demo.service.UserServiceImpl' but was actually of type 'com.sun.proxy.$Proxy76'
需要在启动类指定使用cglib
/**
* proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。
* 如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用
* (这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个
* 属性被省略,那么标准的JDK 基于接口的代理
*
*/
@EnableAsync(proxyTargetClass = true)
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Test
public void pageTest(){
int pageNum = 1;
int pageSize = 2;
Page<User> page = new Page<>(pageNum, pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deleted", 0);
Page<User> userPage = userMapper.selectPage(page, queryWrapper);
System.out.println("记录:");
userPage.getRecords().forEach(v -> {
System.out.println(v);
});
System.out.println("当前页码" + userPage.getCurrent());
System.out.println("总数" + userPage.getTotal());
}
记录:
User(id=5, userName=wt1, password=123456, mail=799957684@qq.com, deleted=0, createTime=2019-12-27T02:37:46, updateTime=2019-12-29T22:43:56)
User(id=6, userName=唐江席1, password=123456, mail=1591226504@qq.com, deleted=0, createTime=2019-12-27T03:22:23, updateTime=2019-12-27T03:22:23)
当前页码1
总数3
可见总数为3,只展示2 条记录,分页成功。
可以看一下Page 里的参数:
总结
总的来说MyBatis-Plus 就是一个增强版的mybatis,它整合很多比较好用的功能,比如类似Example 的动态sql、generate 的自动生成各个层级的代码、还有防攻击等新功能,最关键是MyBatis-Plus 是非侵入的,引入不需要修改旧代码,推荐一试。
Mybatis-Plus 的其他功能后续会继续尝试更新。