MyBatisPlus 入門教程,這篇很贊
- 2022 年 5 月 10 日
- 筆記
- JAVA, springboot
在之前的文章中我們經常使用MybatisPlus進行增刪改查,可能有些小夥伴對mybatisplus不是很熟悉,今天特意出了一般入門級的教程,我自己也是一邊學習一邊寫的,有什麼地方寫的不好的地方請留意指出。
快速入門的小例子
準備資料庫和測試數據
#創建用戶表
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 (1087982257332887553, '大boss', 40, '[email protected]', NULL, '2019-01-11 14:20:20'),
(1088248166370832385, '王天風', 25, '[email protected]', 1087982257332887553, '2019-02-05 11:12:22'),
(1088250446457389058, '李藝偉', 28, '[email protected]', 1088248166370832385, '2019-02-14 08:31:16'),
(1094590409767661570, '張雨琪', 31, '[email protected]', 1088248166370832385, '2019-01-14 09:15:15'),
(1094592041087729666, '劉紅雨', 32, '[email protected]', 1088248166370832385, '2019-01-14 09:48:16');
配置資料庫資訊
在項目的resources目錄下新建application.yml文件,內容如下:
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8
username: root
password: nomore532
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
level:
root: warn
com.demo01.Mapper: trace
pattern:
console: "%p%m%n"
新建實體類型
在項目根目錄下新建一個包,名字為Entity,然後,新建一個名字為User.java的實體類型。
package com.demo01.Entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
//主鍵
private Long id;
//用戶名
private String name;
//郵箱
private String email;
//年齡
private Integer age;
//直屬上級
private Long managerId;
//創建時間
private LocalDateTime createTime;
}
注意:@Data註解能在編譯是自動生成get和set方法。
新建Mapper包,並創建UserMapper介面類。
在項目的根目錄下新建一個名為Mapper包,並創建UserMapper.java介面類,繼承MyBatis-Plus的BaseMapper基類。
package com.demo01.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.demo01.Entity.User;
public interface UserMapper extends BaseMapper<User> {
}
注意:MyBatisPlus的BaseMapper基類需要存入一個泛型,這個泛型是要操作的實體類型。
並在啟動類型添加掃描路徑
@SpringBootApplication
@MapperScan("com.demo01.Mapper")
public class Demo01Application {
public static void main(String[] args) {
SpringApplication.run(Demo01Application.class, args);
}
}
新建測試方法
查詢所有的用戶資訊
package com.demo01;
import com.demo01.Entity.User;
import com.demo01.Mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest(classes = Demo01Application.class)
class Demo01ApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void select() {
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
}
運行結果如下:

通用Mapper
新增(Create)方法
在測試目錄下新建一個測試類,名字為InserTest.java,內容如下:
package com.demo01;
import com.demo01.Entity.User;
import com.demo01.Mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(classes = Demo01Application.class)
class InsertTests {
@Autowired
private UserMapper userMapper;
@Test
public void insert(){
User user = new User();
user.setName("劉強東");
user.setAge(37);
user.setEmail("[email protected]");
user.setManagerId(1087982257332887553L);
int rows = userMapper.insert(user);
System.out.println("影響行數"+rows);
}
}
注意:insert方法需要的參數是一個實體,返回參數是影響行數
運行結果如下:

查資料庫結構如下:

常用註解
@TableName
描述:表名註解
| 屬性 | 類型 | 必須指定 | 默認值 | 描述 |
|---|---|---|---|---|
| value | String | 否 | “” | 表名 |
| schema | String | 否 | “” | schema |
| keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(如果設置了全局 tablePrefix 且自行設置了 value 的值) |
| resultMap | String | 否 | “” | xml 中 resultMap 的 id |
| autoResultMap | boolean | 否 | false | 是否自動構建 resultMap 並使用(如果設置 resultMap 則不會進行 resultMap 的自動構建並注入) |
| excludeProperty | String[] | 否 | {} | 需要排除的屬性名(@since 3.3.1) |
@TableId
描述:主鍵註解
| 屬性 | 類型 | 必須指定 | 默認值 | 描述 |
|---|---|---|---|---|
| value | String | 否 | “” | 主鍵欄位名 |
| type | Enum | 否 | IdType.NONE | 主鍵類型 |
type的類型包括以下幾種:
- AUTO:資料庫ID自增。
- NONE:無狀態,該類型為未設置主鍵類型(註解里等於跟隨全局,全局裡約等於 INPUT)
- INPUT:insert前自行set主鍵值
- ASSIGN_ID:分配ID(主鍵類型為Number(Long和Integer)或String)(since 3.3.0),使用介面
IdentifierGenerator的方法nextId(默認實現類為DefaultIdentifierGenerator雪花演算法) - ASSIGN_UUID :分配UUID,主鍵類型為String(since 3.3.0),使用介面
IdentifierGenerator的方法nextUUID(默認default方法)
TableField
描述:欄位註解(非主鍵)
詳細的註解請查看MybatisPlus的官網
排查非表欄位的三種方式
- transient:不參與序列化
- static
- TableField(exist=false)
MybatisPlus查詢方法(Retrieve)
普遍查詢方法
selectById
@Test
public void selectByIdTest(){
User user = userMapper.selectById(1435065643693645826L);
System.out.println(user);
}
運行結果:

selectBatchIds
@Test
public void selectByIds(){
List<Long> idsList = Arrays.asList(
1088248166370832385L,
1094590409767661570L,
1435065643693645826L
);
List<User> users = userMapper.selectBatchIds(idsList);
users.forEach(System.out::println);
}
運行結果:

selectByMap
@Test
public void selectByMapTest(){
//map.put("name","王天風")
//map.put("age",25)
//where name="王天風" and age=25
Map<String,Object> columnMap = new HashMap<>();
columnMap.put("name","王天風");
columnMap.put("age",25);
List<User> users = userMapper.selectByMap(columnMap);
users.forEach(System.out::println);
}
注意:columnMap中的鍵是資料庫中的欄位,不是實體類型的屬性。
運行結果:

以條件構造器為參數的查詢方法
selectList
/**
* 1、名字中包含雨並且年齡小於40
* name like '%雨%' and age<40
*/
@Test
public void selectByWrapper(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","雨").lt("age",40);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果

/**
* 2、名字中包含雨年並且齡大於等於20且小於等於40並且email不為空
* name like '%雨%' and age between 20 and 40 and email is not null
*/
@Test
public void selectByWrapper2(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","雨").between("age",20,40).isNotNull("email");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

/***
* 3、名字為王姓或者年齡大於等於25,按照年齡降序排列,年齡相同按照id升序排列
* name like '王%' or age>=25 order by age desc,id asc
*/
@Test
public void selectByWrapper3(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.likeRight("name","王").or().ge("age",25).orderByDesc("age").orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

/**
* 4、創建日期為2019年2月14日並且直屬上級為名字為王姓
* date_format(create_time,'%Y-%m-%d')='2019-02-14'
* and manager_id in (select id from user where name like '王%')
*/
@Test
public void selectByWrapper4(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.apply("date_format(create_time,'%Y-%m-%d') = {0}","2019-02-14")
.inSql("manager_id","select id from user where name like '王%'");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

/**
* 5、名字為王姓並且(年齡小於40或郵箱不為空)
* name like '王%' and (age<40 or email is not null)
*/
@Test
public void selectByWrapper5(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.likeRight("name","王").and(wq->wq.lt("age",40).or().isNotNull("email"));
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

/**
* 6、名字為王姓或者(年齡小於40並且年齡大於20並且郵箱不為空)
* name like '王%' or (age<40 and age>20 and email is not null)
*/
@Test
public void selectByWrapper6(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.likeRight("name","王").or(wq->wq.lt("age",40).gt("age",20).isNotNull("email"));
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

/**
* 7、(年齡小於40或郵箱不為空)並且名字為王姓
* (age<40 or email is not null) and name like '王%'
*/
@Test
public void selectByWrapper7(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper.and(wq->wq.lt("age",40).or().isNotNull("email")).and(wq->wq.likeRight("name","王"));
queryWrapper.nested(wq->wq.lt("age",40).or().isNotNull("email"))
.likeRight("name","王");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
};
運行結果:

/**
* 8、年齡為30、31、34、35
* age in (30、31、34、35)
*/
@Test
public void selectByWrapper8(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.in("age",Arrays.asList(30,31,34,35));
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

/**
* 9、只返回滿足條件的其中一條語句即可
* limit 1
*/
@Test
public void selectByWrapper9(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.likeRight("name","王").or(wq->wq.lt("age",40).gt("age",20).isNotNull("email")).last("limit 1");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

select中欄位不全出現的處理方法
/**
* 10、名字中包含雨並且年齡小於40(需求1加強版)
* 第一種情況:select id,name
* from user
* where name like '%雨%' and age<40
* 第二種情況:select id,name,age,email
* from user
* where name like '%雨%' and age<40
*/
@Test
public void selectByWrapper10(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id","name").like("name","雨").lt("age",40);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
@Test
public void selectByWrapper11(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","雨").lt("age",40)
.select(User.class,info-> !info.getColumn().equals("create_time") && !info.getColumn().equals("manager_id")) ;
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

條件構造器中condition作用
condition作用是構造的條件中如何為true就加入,為false就不加入條件。
從AbstractWrapper<T, String, QueryWrapper<T>>的源碼可以看到很多方法都有condition參數,它是一個布爾型的參數,意思就是是否將該sql語句(像in()、like())加在總sql語句上,如下圖所示。

@Test
public void testCondition() {
String name="王";
String email="";
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like(!StringUtils.isEmpty(name),"name",name)
.like(!StringUtils.isEmpty(email),"email",email);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

創建條件構造器是傳入實體對象
@Test
public void selectByWrapperEntity(){
User whereuser = new User();
whereuser.setName("劉紅雨");
whereuser.setAge(32);
QueryWrapper<User> queryWrapper = new QueryWrapper<>(whereuser);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
默認情況下條件是等值,如下圖,如果需要設置為like,需要在實體屬性添加註解。
...省略...
@TableField(condition = SqlCondition.LIKE)
private String name;
...省略...
condition參數可以自定義。
運行結果:

條件構造器中allEq用法
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
個別參數說明:
params:key為資料庫欄位名,value為欄位值
null2IsNull: 為true則在map的value為null時調用 isNull 方法,為false時則忽略value為null的
- 例1:
allEq({id:1,name:"老王",age:null})—>id = 1 and name = '老王' and age is null - 例2:
allEq({id:1,name:"老王",age:null}, false)—>id = 1 and name = '老王'
@Test
public void selectAllEq(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
Map<String,Object> params = new HashMap<String,Object>();
params.put("name","王天風");
params.put("age",25);
queryWrapper.allEq(params);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

其他條件構造器的使用方法
selectMaps
有些時候返回的結果不需要是整個實體類的屬性,可能只需要某幾個欄位的數據,如下:
@Test
public void selectByWrapperMaps(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id","name").like("name","雨").lt("age",40);
List<Map<String,Object>> users = userMapper.selectMaps(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

另外一種需求場景是統計查詢。如下:
/**
* 11、按照直屬上級分組,查詢每組的平均年齡、最大年齡、最小年齡。
* 並且只取年齡總和小於500的組。
* select avg(age) avg_age,min(age) min_age,max(age) max_age
* from user
* group by manager_id
* having sum(age) <500
*/
@Test
public void selectByWrapperMaps2(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("avg(age) avg_age","min(age) min_age","max(age) max_age")
.groupBy("manager_id").having("sum(age)<{0}",500);
List<Map<String,Object>> users = userMapper.selectMaps(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

selectObjs
只返回第一列的數據。
@Test
public void selectByObjs(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id","name").like("name","雨").lt("age",40);
List<Object> users = userMapper.selectObjs(queryWrapper);
users.forEach(System.out::println);
}
運行結果:

selectCount
查詢總記錄數
@Test
public void selectWrapperCount(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","雨").lt("age",40);
int rows = userMapper.selectCount(queryWrapper);
System.out.println("總記錄數:"+rows);
}
運行結果:

Lambda條件構造器
三種方法創建lambda條件構造器:
LambdaQueryWrapper<User> lambda = ``new ``QueryWrapper<User>().lambda()``; ``LambdaQueryWrapper<User> userLambdaQueryWrapper = ``new ``LambdaQueryWrapper<User>()``; ``LambdaQueryWrapper<User> lambdaQuery =Wrappers.<User>``_lambdaQuery_``()``;
@Test
public void selectLambda(){
//LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
//LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>();
LambdaQueryWrapper<User> lambdaQuery =Wrappers.<User>lambdaQuery();
lambdaQuery.like(User::getName,"雨").lt(User::getAge,40);
List<User> users = userMapper.selectList(lambdaQuery);
users.forEach(System.out::println);
}
運行結果:

@Test
public void selectLambda2(){
List<User> users = new LambdaQueryChainWrapper<User>(userMapper)
.like(User::getName, "雨").lt(User::getAge, 40).list();
users.forEach(System.out::println);
}
運行結果:

使用條件構造器的自定義SQL
MP版本需要大於3.0.7
首先在UserMapper類中自定義方法。如下:
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("select * from user ${ew.customSqlSegment}")
List<User> selectAll(@Param(Constants.WRAPPER) Wrapper<User> wrappers);
}
注意:${ew.customSqlSegment}名字是固定寫法。
編寫測試方法
@Test
public void selectMy(){
LambdaQueryWrapper<User> lambdaQuery =Wrappers.<User>lambdaQuery();
lambdaQuery.like(User::getName,"雨").lt(User::getAge,40);
List<User> users = userMapper.selectAll(lambdaQuery);
users.forEach(System.out::println);
}
運行結果如下:

分頁查詢
MP分頁插件實現物理分頁
在項目目錄中新建一個包,名字為config,並創建一個類,名字為MyBatisPlusConfig,內容如下:
package com.demo03.Config;
import com.baomidou.mybatisplus.annotation.DbType;
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;
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
編寫測試實現方法
@Test
public void selectPage(){
LambdaQueryWrapper<User> lambdaQuery =Wrappers.<User>lambdaQuery();
lambdaQuery.gt(User::getAge,20);
Page<User> userPage = new Page<>(1, 2);
Page<User> userPage1 = userMapper.selectPage(userPage, lambdaQuery);
System.out.println(userPage1);
}
AR模式、主鍵策略和基本配置
AR模式
Active Record(活動記錄),是一種領域模型模式,特點是一個模型類對應關係型資料庫中的一個表,而模型類的一個實例對應表中的一行記錄。簡單來說,就是通過實體類操作資料庫的增刪改查。
使用前提需要實體類繼承Model類。如下:
package com.demo03.Entity;
import com.baomidou.mybatisplus.annotation.SqlCondition;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {
//主鍵
private Long id;
//用戶名
@TableField(condition = SqlCondition.LIKE)
private String name;
//郵箱
private String email;
//年齡
private Integer age;
//直屬上級
private Long managerId;
//創建時間
private LocalDateTime createTime;
}
新建測試類
package com.demo03;
import com.demo03.Entity.User;
import com.demo03.Mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ARTests {
@Test
public void insert(){
User user = new User();
user.setName("馬雲");
user.setAge(37);
user.setEmail("[email protected]");
boolean rows = user.insert();
System.out.println("影響行數:"+rows);
}
}
主鍵策略
MP定義了6中主鍵策略。
@Getter
public enum IdType {
/**
* 資料庫ID自增
*/
AUTO(0),
/**
* 該類型為未設置主鍵類型(註解里等於跟隨全局,全局裡約等於 INPUT)
*/
NONE(1),
/**
* 用戶輸入ID
* <p>該類型可以通過自己註冊自動填充插件進行填充</p>
*/
INPUT(2),
/* 以下3種類型、只有當插入對象ID 為空,才自動填充。 */
/**
* 分配ID (主鍵類型為number或string),
* 默認實現類 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花演算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主鍵類型為 string)
* 默認實現類 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER_STR(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
*/
@Deprecated
UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
局部主鍵策略實現
在實體類主鍵通過TableId註解方式。
@TableId(type = IdType.AUTO)
private Long id;
全局主鍵策略實現
在配置文件中配置全局主鍵ID。
mybatis-plus:
global-config:
db-config:
id-type: auto
MP基本配置
詳細資訊查看官網中的配置


