mybatis plus 看这篇就够了,一发入魂
Posted 经理,天台风好大
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis plus 看这篇就够了,一发入魂相关的知识,希望对你有一定的参考价值。
文章目录
兔八哥领读:
一篇写得非常详细的文章,增删改查,各种插件,让你测底熟悉mybatis plus。
mybatis-plus 是一款 Mybatis 增强工具,用于简化开发,提高效率。下文使用缩写 mp 来简化表示 mybatis-plus,本文主要介绍 mp 搭配 SpringBoot 的使用。
注:本文使用的 mp 版本是当前最新的 3.4.2,早期版本的差异请自行查阅文档
官方网站:baomidou.com/
一、快速入门
-
创建一个 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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>mybatis-plus</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mybatis-plus</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置数据库
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yogurt?serverTimezone=Asia/Shanghai
username: root
password: root
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 创建一个实体类
package com.example.mp.po;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
- 创建一个 mapper 接口
package com.example.mp.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mp.po.User;
public interface UserMapper extends BaseMapper<User> { }
- 在 SpringBoot 启动类上配置 mapper 接口的扫描路径
package com.example.mp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mp.mappers")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
- 在数据库中创建表
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
age INT(11) DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
CONSTRAINT manager_fk FOREIGN KEY(manager_id) REFERENCES user (id)
) ENGINE=INNODB CHARSET=UTF8;
INSERT INTO user (id, name, age ,email, manager_id, create_time) VALUES
(1, '大BOSS', 40, 'boss@baomidou.com', NULL, '2021-03-22 09:48:00'),
(2, '李经理', 40, 'boss@baomidou.com', 1, '2021-01-22 09:48:00'),
(3, '黄主管', 40, 'boss@baomidou.com', 2, '2021-01-22 09:48:00'),
(4, '吴组长', 40, 'boss@baomidou.com', 2, '2021-02-22 09:48:00'),
(5, '小菜', 40, 'boss@baomidou.com', 2, '2021-02-22 09:48:00')
- 编写一个 SpringBoot 测试类
package com.example.mp;
import com.example.mp.mappers.UserMapper;
import com.example.mp.po.User;
import org.junit.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 static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
@Autowired
private UserMapper mapper;
@Test
public void testSelect() {
List<User> list = mapper.selectList(null);
assertEquals(5, list.size());
list.forEach(System.out::println);
}
}
- 准备工作完成
数据库情况如下:
项目目录如下:
运行测试类:
可以看到,针对单表的基本 CRUD 操作,只需要创建好实体类,并创建一个继承自BaseMapper的接口即可,可谓非常简洁。并且,我们注意到,User
类中的managerId
,createTime
属性,自动和数据库表中的manager_id
,create_time
对应了起来,这是因为 mp
自动做了数据库下划线命名,到 Java 类的驼峰命名之间的转化。
二、核心功能
(一)注解
mp 一共提供了 8 个注解,这些注解是用在 Java 的实体类上面的。
@TableName
- 注解在类上,指定类和数据库表的映射关系。实体类的类名(转成小写后)和数据库表名相同时,可以不指定该注解。
@TableId
- 注解在实体类的某一字段上,表示这个字段对应数据库表的主键。当主键名为 id 时(表中列名为 id,实体类中字段名为 id),无需使用该注解显式指定主键,mp 会自动关联。若类的字段名和表的列名不一致,可用value属性指定表的列名。另,这个注解有个重要的属性type,用于指定主键策略,参见主键策略小节
@TableField
-
注解在某一字段上,指定 Java 实体类的字段和数据库表的列的映射关系。这个注解有如下几个应用场景。
-
排除非表字段
若 Java 实体类中某个字段,不对应表中的任何列,它只是用于保存一些额外的,或组装后的数据,则可以设置
exist
属性为false
,这样在对实体对象进行插入时,会忽略这个字段。排除非表字段也可以通过其他方式完成,如使用static
或transient
关键字,但个人觉得不是很合理,不做赘述 -
字段验证策略
通过
insertStrategy
,updateStrategy
,whereStrategy
属性进行配置,可以控制在实体对象进行插入,更新,或作为 WHERE 条件时,对象中的字段要如何组装到 SQL 语句中。参见配置小节 -
字段填充策略
通过fill
属性指定,字段为空时会进行自动填充
@Version
- 乐观锁注解,参见乐观锁小节
@EnumValue
- 注解在枚举字段上
@TableLogic
- 逻辑删除,参见逻辑删除小节
KeySequence
- 序列主键策略(oracle)
InterceptorIgnore
- 插件过滤规则
(二)CRUD 接口
mp 封装了一些最基础的 CRUD 方法,只需要直接继承 mp 提供的接口,无需编写任何 SQL,即可食用。mp 提供了两套接口,分别是 Mapper CRUD 接口和 Service CRUD 接口。并且 mp 还提供了条件构造器Wrapper
,可以方便地组装 SQL 语句中的 WHERE 条件,参见条件构造器小节
Mapper CRUD 接口
只需定义好实体类,然后创建一个接口,继承 mp 提供的BaseMapper,即可食用。mp 会在 mybatis 启动时,自动解析实体类和表的映射关系,并注入带有通用 CRUD 方法的 mapper。BaseMapper里提供的方法,部分列举如下:
insert(T entity)
插入一条记录deleteById(Serializable id)
根据主键 id 删除一条记录delete(Wrapper<T> wrapper)
根据条件构造器 wrapper 进行删除selectById(Serializable id)
根据主键 id 进行查找selectBatchIds(Collection idList)
根据主键 id 进行批量查找selectByMap(Map<String,Object> map)
根据 map 中指定的列名和列值进行等值匹配查找selectMaps(Wrapper<T> wrapper)
根据 wrapper 条件,查询记录,将查询结果封装为一个 Map,Map的 key 为结果的列,value 为值selectList(Wrapper<T> wrapper)
根据条件构造器wrapper
进行查询update(T entity, Wrapper<T> wrapper)
根据条件构造器wrapper
进行更新updateById(T entity)
- …
简单的食用示例如前文快速入门小节,下面讲解几个比较特别的方法
selectMaps
BaseMapper
接口还提供了一个selectMaps
方法,这个方法会将查询结果封装为一个 Map,Map 的 key 为结果的列,value 为值
该方法的使用场景如下:
- 只查部分列
当某个表的列特别多,而 SELECT 的时候只需要选取个别列,查询出的结果也没必要封装成 Java 实体类对象时(只查部分列时,封装成实体后,实体对象中的很多属性会是 null),则可以用selectMaps
,获取到指定的列后,再自行进行处理即可
比如
@Test
public void test1() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id","name","email").likeRight("name","黄");
List<Map<String, Object>> maps = mapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
- 进行数据统计
比如
@Test
public void test2() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("manager_id", "avg(age) avg_age", "min(age) min_age", "max(age) max_age")
.groupBy("manager_id").having("sum(age) < {0}", 500);
List<Map<String, Object>> maps = mapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
selectObjs
- 只会返回第一个字段(第一列)的值,其他字段会被舍弃
比如
@Test
public void test3() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name").like("name", "黄");
List<Object> objects = mapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
得到的结果,只封装了第一列的 id
selectCount
- 查询满足条件的总数,注意,使用这个方法,不能调用
QueryWrapper
的select
方法设置要查询的列了。这个方法会自动添加select count(1)
比如
@Test
public void test4() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", "黄");
Integer count = mapper.selectCount(wrapper);
System.out.println(count);
}
Service CRUD 接口
另外一套 CRUD 是 Service 层的,只需要编写一个接口,继承IService
,并创建一个接口实现类,即可食用。(这个接口提供的 CRUD 方法,和 Mapper
接口提供的功能大同小异,比较明显的区别在于IService
支持了更多的批量化操作,如saveBatch
,saveOrUpdateBatch
等方法。
使用示例如下:
- 首先,新建一个接口,继承
IService
package com.example.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.mp.po.User;
public interface UserService extends IService<User> {}
- 创建这个接口的实现类,并继承
ServiceImpl
,最后打上@Service
注解,注册到 Spring 容器中,即可食用
package com.example.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mp.mappers.UserMapper;
import com.example.mp.po.User;
import com.example.mp.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
- 测试代码
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.mp.po.User;
import com.example.mp.service.UserService;
import org.junit.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;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {
@Autowired
private UserService userService;
@Test
public void testGetOne() {
LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery();
wrapper.gt(User::getAge, 28);
User one = userService.getOne(wrapper, false);
System.out.println(one);
}
}
- 结果
另,IService也支持链式调用,代码写起来非常简洁,查询示例如下:
@Test
public void testChain() {
List<User> list = userService.lambdaQuery()
.gt(User::getAge, 39)
.likeRight(User::getName, "黄")
.list();
list.forEach(System.out::println);
}
更新示例如下
@Test
public void Java基础算法看这一篇就够了,简单全面一发入魂
JavaScript面试题看这一篇就够了,简单全面一发入魂(持续更新 step2)