MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,我们假设您已经:
拥有 Java 开发环境以及相应 IDE
熟悉 Spring Boot
熟悉 Maven
1. 创建数据库和表
2.创建项目,初始化项目,使用SpringBoot初始化!
3.导入依赖
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<!--mybatis-plus-->
<!--mybatis-plus是自己开发的,并非官方的!-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
我们使用mybatis-plus 可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus因为版本有差异!
4.在 application.yml
配置文件连接数据库!这一步和mybatis相同!
# 应用名称
spring.application.name=mybatis_plus_01
# 应用服务 WEB 访问端口
server.port=8080
# mysql 5 驱动不同 com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# mysql 8 驱动不同 com.mysql.cj.jdbc.Driver . 需要增加时区的配置 serverTimezone=GMT%2B8
#spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
5.传统的方式pojo-dao(连接mybatis,配置mapper.xml文件)-service-controller
使用了mybatis-plus之后
pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
mapper接口
/**
* 既然对mybatis增强,就要有所行动。
* 如想要增删改查,不需要这样写接口了,
*
* 直接继承BaseMapper<对哪个表做操作,泛型就行哪个表>
* BaseMapper点就去可以看到,所有的增删改查的接口都已经写好了
*/
@Repository //交给我们的spring容器管理,代表它是一个持久层
public interface UserMapper extends BaseMapper<User> {
//所有的CRUD已经基本上完成了
//但是如果想要添加一些独有的方法,也是可以自己写的(编写自己的接口)
//List<User> findAll(); //查询所有
}
注意点:我们需要在主启动类上去扫描我们的mapper包下的所有接口
@SpringBootApplication
@MapperScan("com.lxyk.mapper") //直接扫描路径包所在的接口,如果没有,每个接口都要加@mapper注解 (写完直接去测试)
public class MybatisPlus01Application {
public static void main(String[] args) {
SpringApplication.run(MybatisPlus01Application.class, args);
}
}
测试类中测试
@SpringBootTest
class MybatisPlus01ApplicationTests {
//直接把接口注入进来
@Autowired
private UserMapper userMapper; //不需要写实现类的xml了(单表不用)
@Test
void contextLoads() {
//直接调用查询方法,但是这个方法中有这个参数是我们没有的 Wrapper<T>(它代表的是实体对象封装类,可以为null)
userMapper.selectList(null).forEach(System.out::println);//返回一个list,所以遍历
}
}
配置日志
# 配置日志 (系统自带的,控制台输出)
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
2. 主键生成策略
主键自增
我们需要配置主键自增:
-
实体类字段上
@TableId(type = IdType.AUTO)
-
数据库字段上一定是自增的
AUTO, //数据库id自增 NONE, //为设置主键 INPUT, // 手动输入 自己写id ID_WORKER, // 默认的全局唯一id UUID, // 全局唯一id UUID ID_WORKER_STR; // ID_WORKER 字符串的表示
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
//对应数据库中的主键(uuid,自增id,雪花算法,redis,zookeeper!)
@TableId(type = IdType.INPUT) //一旦收到输入id之后,就需要自己配置id了!
private Long id;
private String name;
private Integer age;
private String email;
}
//测试插入
@Test
public void testInsert() {
User user = new User(6L,"fbb4",18,"test9@baomidou.com");
System.out.println(userMapper.insert(user));
}
自动填充
创建时间、修改时间!这些个操作一般都是自动化完成的,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified几乎所有的表都要配置上!而且需要自动化!
我们需要在实体类中定义两个字段
private Date createTime;
private Date updateTime;
然后在字符段的上方加入注解
// 插入填充字段
@TableField(.. fill = FieldFill.INSERT)
private Date createTime;
// 插入和更新填充字段
@TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入填充字段
*/
INSERT,
/**
* 更新填充字段
*/
UPDATE,
/**
* 插入和更新填充字段
*/
INSERT_UPDATE
在表中新增字段 create_time,update_time
编写一个类的处理器来处理这个注解即可
@Slf4j //日志注解
@Component // 一定不要忘记把处理器加到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入时候的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ...");
// setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
// 更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ...");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
继承MetaObjectHandler,实现它的两个方法
乐观锁(面试常问)
乐观锁:顾名思义,它总是认为不会出现问题,无论干什么都不去上锁!如果出现了问题,再次更新值测试!
悲观锁:顾名思义,它总是认为总是出现问题,无论干什么都上锁!再去操作!
.配置插件
spring xml 方式:
<bean class="com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor" id="optimisticLockerInnerInterceptor"/>
<bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
<property name="interceptors">
<list>
<ref bean="optimisticLockerInnerInterceptor"/>
</list>
</property>
</bean>
spring boot 注解方式:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
然后需要在实体类和数据库当中添加字段
@Version private Integer version; //乐观锁
注册组件
// 扫描我们的 mapper文件夹
@MapperScan("com.lxyk.mapper")
@EnableTransactionManagement //开启事务支持
@Configuration //配置类
public class MyBatisPlusConfig {
// 注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
// Spring Boot 方式
@Configuration
@MapperScan("按需修改")
public class MybatisPlusConfig {
/**
* 旧版
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 新版
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
新旧版本对比,案例是旧版
测试一下
// 测试乐观锁成功
@Test
public void testVersionSuccess(){
// 1. 查询用户信息
User user = userMapper.selectById(1L);
// 2. 修改用户信息
user.setName("fbb");
user.setAge(24);
// 3. 执行更新操作
userMapper.updateById(user);
}
// 测试乐观锁失败!多线程下
@Test
public void testVersionFall(){
// 线程1
User user1 = userMapper.selectById(1L);
user1.setName("fan111");
user1.setAge(14);
// 线程2
User user2 = userMapper.selectById(1L);
user2.setName("fan222");
user2.setAge(24);
userMapper.updateById(user2);
//自旋锁来多次尝试提交!
userMapper.updateById(user1); //如果没有乐观锁就会覆盖插队线程的值
}
乐观锁成功
乐观锁失败
查询操作
//测试查询
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
// 批量查询
@Test
public void testSelectByBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
// 按照条件查询之一使用 map
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
// 自定义要查询
map.put("name","冯宝宝说java");
map.put("age",3);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
分页查询
1.配置拦截器
// 分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
可以和之前的乐观锁注册写在一起
2.直接使用Page对象即可
//分页
@Test
public void test6(){
// 当前页
// 每页显示条数
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
System.out.println(page.getTotal()); //总数
}
删除操作
删除分两种,物理删除和逻辑删除
物理删除是真删除
逻辑删除不会真的删除,查询是查不到的,但数据依然存在于数据库中,只是字段状态改变了
- 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
- 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
逻辑删除:
1.配置文件application.yml
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)
2. 实体类字段上加上@TableLogic
注解(数据库也要加)
@TableLogic
private Integer deleted;
3.注册层去注册逻辑删除(和乐观锁、分页写在一起)
//逻辑删除
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
// 测试删除
@Test
public void testDeleteById(){
userMapper.deleteById(1481878928972926978L);
}
// 测试批量删除
@Test
public void testDeleteBatchId(){
userMapper.deleteBatchIds(Arrays.asList(1287326823914405893L,1287326823914405894L));
}
//通过map删除
@Test
public void testDeleteByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","冯宝宝说java");
userMapper.deleteByMap(map);
}
性能插件
sql执行效率插件
它可以执行检测每条sql语句大概在什么范围,是否需要优化
1.首先要注册(在注册层里写)
@Bean
@Profile({"dev","test"}) //设置dev(开发) test(测试) 环境开启,保证我们的效率 (在yml文件中开启设定)
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100); //(ms==毫秒) 设置sql执行的最大时间,如果超过了则不执行
performanceInterceptor.setFormat(true); // 是否格式化
return performanceInterceptor;
}
2.配置文件application.yml
在spring:中加入
profiles: active: dev
spring:
application:
name: mybatis_plus_01
datasource:
driver-class-name: com.mysql.jdbc.Driver
password: root
url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
username: root
# 开启 设置 SQL执行效率插件
profiles:
active: dev
测试一下
@Test
public void test10(){
//这条sql如果在设定的10毫秒之内, 就正常查出来,如果超时就会抛异常
userMapper.selectList(null).forEach(System.out::println);
}
注意:
打开浏览器,搜索任意数据,点进去后不会超过2秒。因为在实际开发中,超过两秒就要去修改这个接口了(涉及到sql优化)sql语句优化的30种方法 - littlecarzz - 博客园
条件构造器
可按条件去查询
//条件构造器
@Test
public void test11(){
//查询name不为空,邮箱不为空,年龄大于10岁
QueryWrapper<User> Wrapper = new QueryWrapper<>();
Wrapper.isNotNull("name").isNotNull("email").gt("age",10); //查询条件
userMapper.selectList(Wrapper).forEach(System.out::println);
}
使用 QueryWrapper 是mybatis-plus 不写SQL就可以完成查询的一个工具类
https://www.csdn.net/tags/NtzaEg2sMTMxNjYtYmxvZwO0O0OO0O0O.html
条件构造器 | MyBatis-PlusMyBatis-Plus 官方文档https://baomidou.com/pages/10c804/#abstractwrapper打开官网,搜索条件构造器,便可知道有那些方法来帮助查询
部分方法演示:
package com.lxyk;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lxyk.mapper.UserMapper;
import com.lxyk.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootTest
class MybatisPlus01ApplicationTests {
//条件构造器
//isNotNull
@Test
public void test11(){
//查询name不为空,邮箱不为空,年龄大于10岁
QueryWrapper<User> Wrapper = new QueryWrapper<>();
Wrapper.isNotNull("name").isNotNull("email").gt("age",10); //查询条件
userMapper.selectList(Wrapper).forEach(System.out::println);
}
//eq
@Test
public void test12(){
//查询姓名为Tom
QueryWrapper<User> Wrapper = new QueryWrapper<>();
Wrapper.eq("name","Tom");
User user = userMapper.selectOne(Wrapper);//多条数据List或者map
System.out.println("user="+user);
}
//between
@Test
public void test13(){
//查询数据之前的区间 如:20------30之间的用户
QueryWrapper<User> Wrapper = new QueryWrapper<>();
Wrapper.between("age",20,30);
Integer Count = userMapper.selectCount(Wrapper);
System.out.println("Count="+Count);
}
//notLike
@Test
public void test14(){
//查询name 不包含m的 邮箱是t开头的
QueryWrapper<User> Wrapper = new QueryWrapper<>();
Wrapper.notLike("name","m").likeRight("email","t"); //likeRight == '值%';likeLeft == '%值';notLike == ‘%值%’
List<Map<String, Object>> Count = userMapper.selectMaps(Wrapper);//多条数据List或者map
Count.forEach(System.out::println);
}
//inSql
@Test
public void test15(){
//查询name 不包含m的 邮箱是t开头的
QueryWrapper<User> Wrapper = new QueryWrapper<>();
Wrapper.inSql("id","select id from user where id < 3"); //id小于3的语句
List<Object> list = userMapper.selectObjs(Wrapper);
list.forEach(System.out::println);
}
//排序(升或倒)
@Test
public void test16(){
//查询name 不包含m的 邮箱是t开头的
QueryWrapper<User> Wrapper = new QueryWrapper<>();
//Wrapper.orderByDesc("id"); //倒序
Wrapper.orderByAsc("id"); //升序
List<User> list = userMapper.selectList(Wrapper);
list.forEach(System.out::println);
}
}