MyBatisPlus使用

一、MP 是什么

MP 全称 Mybatis-Plus ,套用官方的解释便是成为 MyBatis 最好的搭档,简称基友。它是在MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

MyBatisPlus使用_第1张图片

1. 三大特性

1)润物无声

只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。

2)效率至上

只需简单配置,即可快速进行单表 CRUD 操作,从而节省大量时间。

3)丰富功能

代码生成、物理分页、性能分析等功能一应俱全。

2. 支持数据库

mysql 、mariadb 、oracle 、db2 、h2 、hsql 、sqlite 、postgresql 、sqlserverpresto 、Gauss 、Firebird

Phoenix 、clickhouse 、Sybase ASE 、 OceanBase 、达梦数据库 、虚谷数据库 、人大金仓数据库 、南大通用数据库

3. 框架结构

MyBatisPlus使用_第2张图片

pom 依赖


MyBatisPlus使用_第3张图片

配置文件


MyBatisPlus使用_第4张图片


实体类


MyBatisPlus使用_第5张图片

Mapper


测试类


MyBatisPlus使用_第6张图片

2.  CRUD 基操

1)insert

@Test

publicvoidinsert(){

//这里使用了 lombok 中的建造者模式构建对象

User user = User.builder().deptId(1).name("小华").remark("小华爱学习").build();

intinsertFlag = userMapper.insert(user);

log.info("插入影响行数,{} | 小华的ID: {}", insertFlag, user.getId());

}

/** OUTPUT:

插入影响行数,1 | 小华的ID: 8

**/

可以看到我们不仅插入了数据,而且还获取到了插入数据的ID,但是值得注意的是这里的ID 虽然是自增的,但并非是 MP 默认的 ID生成策略,而是我们在实体类中指定的:

MyBatisPlus使用_第7张图片

在 MP 中支持的主键生成策略有以下几种:

MyBatisPlus使用_第8张图片

我们既然已经看到了 @TableId 这个注解,那我们再来关注一个常用注解 @TableField

从注解名上我们就可以看出,@TableId 是用来标记主键 ID 的,而 @TableField 是用来标记其他字段的。

MyBatisPlus使用_第9张图片

可以看得出来这个注解中存在的值还是比较多的,下面介绍几个常用的值:

value

用于解决字段名不一致问题和驼峰命名,比如实体类中属性名为 remark,但是表中的字段名为 describe ,这个时候就可以使用 @TableField(value="describe") 来进行转换。驼峰转换如果在全局中有配置驼峰命名,这个地方可不写。

exist

用于在数据表中不存在的字段,我们可以使用 @TableField(exist = false) 来进行标记

condition

用在预处理 WHERE 实体条件自定义运算规则,比如我配置了 @TableField(condition = SqlCondition.LIKE),输出 SQL 为:select 表 where name LIKE CONCAT('%',值,'%'),其中 SqlCondition 值如下:

MyBatisPlus使用_第10张图片

update

用在预处理 set 字段自定义注入,比如我配置了 @TableField(update = "%s+1"),其中%s 会填充字段,输出 SQL 为:update 表名 set 字段 = 字段+1 where 条件

select

用于是否查询时约束,如果我们有个字段 remark 是 text 类型的,查询的时候不想查询该字段,那么就可以使用 @TableField(select = false) 来约束查询的时候不查询该字段

2)update

MybatisPlus 的更新操作存在两种:

intupdateById(Param("et")T entity);

intupdate(@Param("et")T entity, @Param("ew")Wrapper updateWrapper);

根据 ID 更新

@Test

publicvoidupdate(){

User user = User.builder().id(3).name("小华").remark("小华爱玩游戏").build();

userMapper.updateById(user);

}

/** 更新结果:

User(id=3, deptId=1, name=小华, remark=小华爱玩游戏)

**/

根据条件更新

@Test

publicvoidupdate(){

UpdateWrapper updateWrapper =newUpdateWrapper<>();

updateWrapper.eq("name","小华").set("remark","小华爱下棋");

userMapper.update(null, updateWrapper);

}

/** 更新结果:

User(id=3, deptId=1, name=小华, remark=小华爱下棋)

**/

我们也可以将要更新的条件放进 user 对象 里面:

@Test

publicvoidupdate(){

UpdateWrapper updateWrapper =newUpdateWrapper<>();

updateWrapper.eq("name","小华");

User user = User.builder().remark("小华爱游泳").build();

userMapper.update(user, updateWrapper);

}

/** 更新结果:

User(id=3, deptId=1, name=小华, remark=小华爱游泳)

**/

3)delete

在 MybatisPlus 中删除的方式相对于更新多,总共有四种:

intdeleteById(Serializable id);

intdeleteByMap(@Param("cm")Map columnMap);

intdelete(@Param("ew")Wrapper wrapper);

intdeleteBatchIds(@Param("coll")Collection idList);

根据 ID 删除

@Test

publicvoiddeleteById(){

userMapper.deleteById(3);

}

/** SQL语句:

DELETE FROM student WHERE id = 3;

**/

根据 Map 删除

@Test

publicvoiddeleteByMap(){

HashMap columnMap =newHashMap<>();

columnMap.put("name","小华");

columnMap.put("remark","小华爱游泳");

userMapper.deleteByMap(columnMap);

}

/** SQL语句:

DELETE FROM student WHRE name = '小华' AND remark = '小华爱游泳';

**/

根据 Wrapper 删除

@Test

publicvoiddelete(){

UpdateWrapper wrapper =newUpdateWrapper<>();

wrapper.eq("remark","小华爱下棋");

userMapper.delete(wrapper);

}

/** SQL语句:

DELETE FROM student WHRE remark = '小华爱下棋';

**/

根据 Wrapper 删除还有另外一种方式,直接将实体类放入 Wrapper 中包装:

@Test

publicvoiddelete(){

User user = User.builder().remark("小华爱下棋").build();

UpdateWrapper wrapper =newUpdateWrapper<>(user);

userMapper.delete(wrapper);

}

/** SQL语句:

DELETE FROM student WHRE remark = '小华爱下棋';

**/

根据 ID 批量删除

@Test

publicvoiddeleteBatchIds(){

List idList =newArrayList<>();

idList.add(4);

idList.add(7);

userMapper.deleteBatchIds(idList);

}

/** SQL语句:

DELETE FROM student WHERE id In (4,7)

**/

4)select

查询操作在我们开发中是最经常用到的,也是重中之重。MybatisPlus 中支持查询的方法也比较多,如下:

TselectById(Serializable id);

ListselectBatchIds(@Param("coll")Collection idList);

ListselectByMap(@Param("cm")Map columnMap);

TselectOne(@Param("ew")Wrapper queryWrapper);

IntegerselectCount(@Param("ew")Wrapper queryWrapper);

ListselectList(@Param("ew")Wrapper queryWrapper);

List> selectMaps(@Param("ew") Wrapper queryWrapper);

ListselectObjs(@aram("ew")Wrapper queryWrapper);

IPageselectPage(IPage page, @Param("ew")Wrapper queryWrapper);

IPage> selectMapsPage(IPage page,@Param("ew") Wrapper queryWrapper);

可以看到总共有 10 个方法,我们接下来一个一个测试

查询所有

@Test

publicvoidselectList(){

List users = userMapper.selectList(null);

users.forEach(System.out::println);

}

/**

OUTPUT:

User(id=1, deptId=1, name=小菜, remark=关注小菜不迷路!)

User(id=2, deptId=1, name=小明, remark=好好学习,天天向上!)

SQL语句:

SELECT id, dept_id, name, remark FROM student;

**/

查询数量

@Test

publicvoidselectCount(){

QueryWrapper queryWrapper =newQueryWrapper<>();

queryWrapper.like("name","小");

System.out.println(userMapper.selectCount(queryWrapper));

}

/**

OUTPUT:

2

SQL语句:

SELECT COUNT( 1 ) FROM student WHERE (name LIKE '%小%');

**/

根据 ID 查询

@Test

publicvoidselectById(){

User user = userMapper.selectById(1);

System.out.println(user);

}

/**

OUTPUT:

User(id=1, deptId=1, name=小菜, remark=关注小菜不迷路!)

SQL语句:

SELECT id, dept_id, name, remark FROM student WHERE ID = 1;

**/

根据 ID 批量查询

@Test

publicvoidselectBatchIds(){

List users = userMapper.selectBatchIds(Arrays.asList(1,2));

users.forEach(System.out::println);

}

/**

OUTPUT:

User(id=1, deptId=1, name=小菜, remark=关注小菜不迷路!)

User(id=2, deptId=1, name=小明, remark=好好学习,天天向上!)

SQL语句:

SELECT id, dept_id, name, remark FROM student WHERE ID IN (1, 2);

**/

根据条件查询单条

@Test

publicvoidselectOne(){

QueryWrapper queryWrapper =newQueryWrapper<>();

queryWrapper.eq("name","小菜");

User user = userMapper.selectOne(queryWrapper);

System.out.println(user);

}

/**

OUTPUT:

User(id=1, deptId=1, name=小菜, remark=关注小菜不迷路!)

SQL语句:

SELECT id, name, dept_id, remark FROM student WHERE (name = '小菜');

**/

根据条件查询多条

通过 map 传递参数,不是通过 LIKE 查询,而是通过 = 查询

@Test

publicvoidselectByMap(){

HashMap columnMap =newHashMap<>();

columnMap.put("name","小");

List users = userMapper.selectByMap(columnMap);

users.forEach(System.out::println);

}

/**

OUTPUT:

null

SQL语句:

SELECT id, name, dept_id, remark FROM student WHERE name = '小';

**/

如果我们没有新建实体类进行结果封装,我们还可以用 Map 来接收结果集:

@Test

publicvoidselectMaps(){

QueryWrapper queryWrapper =newQueryWrapper<>();

queryWrapper.like("name","小");

List> maps = userMapper.selectMaps(queryWrapper);

maps.forEach(System.out::println);

}

/**

OUTPUT:

{name=小菜, remark=关注小菜不迷路!, id=1, dept_id=1}

{name=小明, remark=好好学习,天天向上!, id=2, dept_id=1}

SQL语句:

SELECT id, name, dept_id, remark FROM student WHERE (name LIKE '%小%');

**/

也可以用 Object 对象来接收结果集:

@Test

publicvoidselectObjs(){

QueryWrapper queryWrapper =newQueryWrapper<>();

queryWrapper.like("name","小");

List objects = userMapper.selectObjs(queryWrapper);

}

/**

OUTPUT:

{name=小菜, remark=关注小菜不迷路!, id=1, dept_id=1}

{name=小明, remark=好好学习,天天向上!, id=2, dept_id=1}

SQL语句:

SELECT id, name, dept_id, remark FROM student WHERE (name LIKE '%小%');

**/

分页查询

@Test

publicvoidselectPage(){

QueryWrapper queryWrapper =newQueryWrapper<>();

queryWrapper.like("name","小");

Page page =newPage<>(1,1);

IPage userIPage = userMapper.selectPage(page, queryWrapper);

System.out.println("数据总数:"+ userIPage.getTotal());

System.out.println("总页数:"+ userIPage.getPages());

System.out.println("当前页:"+ userIPage.getCurrent());

System.out.println("页大小:"+ userIPage.getSize());

userIPage.getRecords().forEach(System.out::println);

}

/**

OUTPUT:

数据总数:2

总页数:2

当前页:1

页大小:1

User(id=1, deptId=1, name=小菜, remark=关注小菜不迷路!)

SQL语句:

SELECT id, name, dept_id, remark

FROM student

WHERE (name LIKE '%小%')

LIMIT 0,1;

**/

3. 条件构造器

MyBatisPlus使用_第11张图片

在 CRUD 的基本操作中,我们想要通过条件查询都是通过 Wrapper 类进行封装的,上面只是简单的用到 eq 和 like 操作。事实上这个类十分强大,我们在下面会详细进行介绍。

1)allEq

全部 eq 或个别 isNull

allEq(Map params)

allEq(Map params,booleannull2IsNull)

allEq(booleancondition, Map params,booleannull2IsNull)

allEq(BiPredicate filter, Map params)

allEq(BiPredicate filter, Map params,booleannull2IsNull)

allEq(booleancondition, BiPredicate filter, Map params,booleannull2IsNull)

参数说明:

param: key 为数据库字段名,value 为字段值

**nullsIsNull:**为 true 则在 map 的 value 为 null 时调用 isNull 方法,为 false 时则忽略 value 为 null 时不调用 isNull 方法

filter: 过滤函数,判断是否允许字段传入比对条件中

使用示例:

allEq(Map params)

@Test

publicvoidtestAllEq(){

QueryWrapper queryWrapper =newQueryWrapper<>();

Map params =newHashMap<>();

params.put("name","小菜");

params.put("dept_id",1);

params.put("remark",null);

queryWrapper.allEq(params);//会调用 isNull 方法

userMapper.selectList(queryWrapper);

}

/**

结果:

{}

SQL语句:

SELECT id,name,dept_id,remark

FROM student

WHERE (name = '小菜' AND dept_id = 1 AND remark IS NULL);

**/

allEq(Map params, boolean null2IsNull)

@Test

publicvoidtestAllEq(){

QueryWrapper queryWrapper =newQueryWrapper<>();

Map params =newHashMap<>();

params.put("name","小菜");

params.put("dept_id",1);

params.put("remark",null);

queryWrapper.allEq(params,false);//不会调用 isNull 方法

userMapper.selectList(queryWrapper);

}

/**

结果:

User(id=1, deptId=1, name=小菜, remark=关注小菜不迷路!)

SQL语句:

SELECT id,name,dept_id,remark

FROM student

WHERE (name = '小菜' AND dept_id = 1);

**/

allEq(boolean condition, Map params, boolean null2IsNull)

@Test

publicvoidtestAllEq(){

QueryWrapper queryWrapper =newQueryWrapper<>();

Map params =newHashMap<>();

params.put("name","小菜");

params.put("dept_id",1);

params.put("remark",null);

queryWrapper.allEq(false,params,false);//不会带入条件进行查询

userMapper.selectList(queryWrapper);

}

/**

结果:

{name=小菜, remark=关注小菜不迷路!, id=1, dept_id=1}

{name=小明, remark=好好学习,天天向上!, id=2, dept_id=1}

SQL语句:

SELECT id,name,dept_id,remark

FROM student;

**/

allEq(BiPredicate filter, Map params)

@Test

publicvoidtestAllEq(){

QueryWrapper queryWrapper =newQueryWrapper<>();

Map params =newHashMap<>();

params.put("name","小菜");

params.put("dept_id",1);

params.put("remark",null);

//只有 key 中含有 “m” 才会用作条件判断

queryWrapper.allEq((k, v) -> (k.contains("m")), params);

userMapper.selectList(queryWrapper);

}

/**

结果:

0

SQL语句:

SELECT id,name,dept_id,remark

FROM student

WHERE (name = '小菜' AND remark IS NULL);

**/

2)比较操作

eq: 相当于 =

ne: 相当于 !=

gt: 相当于 >

ge: 相当于>=

lt: 相当于 <

le: 相当于<=

between: 相当于between ... and ...

notBetween: 相当于not between ... and ...

in: 相当于in(.., .., ..)

notIn: 相当于not in(.., .., ..)

3)模糊查询

like: like("name","小菜") --> name like "%小菜%"

notLike: notLike("name","小菜") --> name not like "%小菜%"

likeLeft: like("name","小菜") --> name like "%小菜"

likeRight: like("name","小菜") --> name like "小菜%"

4)排序

orderBy:

orderBy(booleancondition,booleanisAsc, R... columns)

orderBy(true, true, "id", "name") --> order by id ASC, name ASC

orderByAsc:

orderByAsc("id","name") --> order by id ASC, name ASC

orderByDesc:

orderByDesc("id","name) --> order by id Desc, name Desc

5)逻辑查询

or:

拼接:主动调用 or 表示紧接着下一个方法不是用 and 连接!(不调用 or 则默认为使用 and连接), eq("id",1).or().eq("name","老王")

嵌套:or(i -> i.eq("name", "李白").ne("status", "活着"))

and:

嵌套:and(i -> i.eq("name", "李白").ne("status", "活着"))

6)select

在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段,如select("id", "name")

4. 配置讲解

1)基本配置

configLocation

用于指明 **MyBatis ** 配置文件的位置,如果我们有 MyBatis 的配置文件,需将配置文件的路径配置到 configLocation 中

SpringBoot:

mybatis-plus.config-location = classpath:mybatis-config.xml

SpringMvc:

class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">

mapperLocations

用于指明 Mapper 所对应的 XML 的文件位置,我们在 通用 CRUD 中用到的 Mapper 是直接继承 MP 提供的 BaseMapper ,我们也可以自定义方法,然后在 XML 文件中自定义SQL,而这时我们需要告诉 Mapper 所对应 XML 文件的位置

SpringBoot:

mybatis-plus.mapper-locations = classpath*:mybatis/*.xml

SpringMVC:

class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">

typeAliasesPackage

用于 MyBatis 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper对应的 XML 文件中可以直接使用类名,而不用使用全限定的类名

SpringBoot:

mybatis-plus.type-aliases-package = cbuc.life.bean

SpringMVC:

class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">

value="com.baomidou.mybatisplus.samples.quickstart.entity"/>

2)进阶配置

mapUnderScoreToCamelCase

是否开启自动驼峰命名规则映射,这个配置的默认值是 true,但是这个属性在 MyBatis 中的默认值是 false,所以在我们平时的开发中都会将这个配置开启。

#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在

mybatis-plus.configuration.map-underscore-to-camel-case = false

cacheEnabled

全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true

mybatis-plus.configuration.cache-enabled = false

3)DB 策略配置

idType

全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。该配置的默认值为 ID_WORKER

SpringBoot:

mybatis-plus.global-config.db-config.id-type = auto

SpringMVC:

class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">

tablePrefix

表名前缀,全局配置后可省略@TableName()配置。该配置的默认值为 null

SpringBoot:

mybatis-plus.global-config.db-config.table-prefix = yq_

SpringMVC:

class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">

5. 其他扩展

1)自动填充

有时候我们在插入或更新数据的时候,希望有些字段可以自动填充。比如我们平时数据表里面会有个 插入时间 或者 更新时间 这种字段,我们会默认以当前时间填充,在 MP 中我们也可以进行配置。

首先我们需要借助 @TableField(fill = FieldFill.INSERT) 这个注解,在插入时进行填充。

@TableField(fill = FieldFill.INSERT)

privateString remark;

其中自动填充的模式如下:

publicenumFieldFill {

/**

* 默认不处理

*/

DEFAULT,

/**

* 插入时填充字段

*/

INSERT,

/**

* 更新时填充字段

*/

UPDATE,

/**

* 插入和更新时填充字段

*/

INSERT_UPDATE

}

然后我们再编写自定义的填充处理模式:

@Component

publicclassMyMetaObjectHandlerimplementsMetaObjectHandler{

@Override

publicvoidinsertFill(MetaObject metaObject){

Object remark = getFieldValByName("remark", metaObject);

if(null== remark) {

setFieldValByName("remark","好好学习", metaObject);

}

}

@Override

publicvoidupdateFill(MetaObject metaObject){

//自定义更新时填充

}

}

测试:

@Test

publicvoidtestObjectHandler(){

User user = User.builder().deptId(1).name("小明").build();

userMapper.insert(user);

}

/**

SQL语句:

INSERT INTO student ( name, dept_id, remark )

VALUES ( '小明', 1, '好好学习' );

**/

可以看到插入时,已经自动将我们填充的字段合并进去。

2)逻辑删除

在开发中,很多时候我们删除数据并不需要真正意义上的物理删除,而是使用逻辑删除,这样子查询的时候需要状态条件,确保被标记的数据不被查询到。MP 当然也支持这样的功能。

我们需要先为 student 表添加一个字段 status 来声明数据是否被删除,0 表示被删除,1表示未删除,然后也需要在实体类上增加这个属性:

@TableLogic

privateInteger status;

在 application.yaml 中配置:

mybatis-plus:

global-config:

db-config:

logic-delete-value:0

logic-not-delete-value:1

测试:

@Test

publicvoidtestLogicDelete(){

userMapper.deleteById(1);

}

/**

SQL语句:

UPDATE student SET status=0

WHERE id=1 AND status=1;

**/

可以看出这段 SQL 并没有真正删除,而是进行了逻辑删除,只是更新了删除标识

3)通用枚举

如果有性别之类的字段,我们通常会用 0 和 1 来表示,但是查出来我们得进行值转换,这个时候我们就可以使用枚举来解决这个问题:

首先为 student 表添加一个 sex 字段来表示性别,0 表示女性,1 表示男性,然后定义一个枚举类:

publicenumSexEnum implements IEnum {

MAN(1,"男"),

WOMEN(0,"女");

privateintcode;

privateString value;

SexEnum(intcode, String value) {

this.code = code;

this.value = value;

}

@Override

publicIntegergetValue(){

returnthis.code;

}

//注意要重写此方法,不然会将值转换成 ‘MAN’,而不是 ‘男’

@Override

publicStringtoString(){

returnthis.value;

}

}

然后在实体类中添加对应属性:

privateSexEnum sex;

在 application.yaml 中配置:

mybatis-plus:

type-enums-package:cbuc.life.enums

测试:

@Test

publicvoidselectOne(){

QueryWrapper queryWrapper =newQueryWrapper<>();

queryWrapper.eq("name","小菜");

User user = userMapper.selectOne(queryWrapper);

System.out.println(user);

}

/**

输出结果:

User(id=1, deptId=1, name=小菜, remark=关注小菜不迷路!, status=1, sex=男)

SQL语句:

SELECT id,sex,name,dept_id,remark,status

FROM student

WHERE status=1 AND (name = '小菜');

**/

你可能感兴趣的:(MyBatisPlus使用)