MybatisPlus

目录

[1 引入MP依赖代替Mybatis依赖](#1 引入MP依赖代替Mybatis依赖)

[2 配置yaml](#2 配置yaml)

[3 继承BaseMapper](#3 继承BaseMapper)

[3.1 默认约定](#3.1 默认约定)

[3.2 自定义约定](#3.2 自定义约定)

[3.3 继承BaseMapper的常用方法](#3.3 继承BaseMapper的常用方法)

[3.3.1 增](#3.3.1 增)

[3.3.2 删](#3.3.2 删)

[3.3.3 改](#3.3.3 改)

[3.3.4 查](#3.3.4 查)

[4 条件构造器](#4 条件构造器)

[4.1 QueryWrapper](#4.1 QueryWrapper)

[4.2 UpdateWrapper](#4.2 UpdateWrapper)

[4.3 AbstractLambdaWrapper(推荐)](#4.3 AbstractLambdaWrapper(推荐))

[4.4 自定义sql方法](#4.4 自定义sql方法)

[4.4.1 dao接口+xml](#4.4.1 dao接口+xml)

[4.4.2 用法示例](#4.4.2 用法示例)

[5 IService接口](#5 IService接口)

[5.1 Iservice常用方法](#5.1 Iservice常用方法)

[5.2 使用流程](#5.2 使用流程)

[5.3 用法示例](#5.3 用法示例)

[5.4 lambdaQuery()方法、lambdaUpdate()方法](#5.4 lambdaQuery()方法、lambdaUpdate()方法)

[5.5 批量操作](#5.5 批量操作)

[5.5.1 批量操作对比](#5.5.1 批量操作对比)

[5.5.2 代码示例](#5.5.2 代码示例)

[6 多表查询](#6 多表查询)


1 引入MP依赖代替Mybatis依赖

XML 复制代码
<!--MybatisPlus(包含了mybatis的starter)-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.4</version>
</dependency>

2 配置yaml

(见之前文章SpringBoot (2) yaml,整合项目)

3 继承BaseMapper

BaseMapper泛型为所要操作的pojo类,MP依据此泛型通过反射得到类的字节码文件,然后通过字节码信息反推出数据库中的表信息,反推要遵循约定

java 复制代码
public interface TeacherDao extends BaseMapper<Teacher> {
}

3.1 默认约定

(1) 类名的驼峰转下划线作为表名: 类名HighSchool反推出的表名就是high_school

(2) 名为id的成员变量作为主键字段

(3) 类成员变量的驼峰转下划线作为字段名

3.2 自定义约定

@TableName用在pojo类上,指定表名

@TableId(value="id", type=IdType.AUTO)用在pojo类成员变量上,指定表主键字段

IdType.AUTO表示自增Id,数据库自动生成

IdType.INPUT表示set方法设置,用户自己填写

IdType.ASSING_ID(默认)表示MP会自动利用雪花算法生成long类型的整数,长度20位

@TableField用在pojo类成员变量上,指定普通字段

(1) 对于is开头并且是boolean类型的pojo成员变量,在通过反射机制会将is去掉,那么就起不到驼峰转下划线的效果,因此is开头的boolean类型的成员变量必须用@TableField注解

(2) 对于关键字的pojo成员变量,必须用@TableField注解,并且要将关键字写在反引号``中

(3) pojo成员变量不是数据库字段,必须用@TableField(exist=false)注解

java 复制代码
@TableName("aboluo_person")
public class Person {
    @TableId
    private Long id;
    @TableField("user_name")
    private String name;
    @TableField("is_man")
    private Boolean isMan;
    @TableField("`select`")
    private Integer select;
    @TableField(exist = false)
    private String other;
}

3.3 继承BaseMapper的常用方法

3.3.1 增

teacherDao.insert(T); //这里的"泛型类"就是BaseMapper的泛型

3.3.2 删

teacherDao.deleteById(5)

teacherDao.delete(Wrapper)

teacherDao.deleteBatchIds(Collection<?>)

3.3.3 改

teacherDao.updateById(T) //非空字段才更新(可以在yaml中设置)

teacherDao.update(Wrapper<T>)

teacherDao.update(T,Wrapper<T>)

3.3.4 查

单查询:

teacherDao.selectOne(Wrapper<T>);

teacherDao.selectById(6);

多查询:

teacherDao.selectList(Wrapper<T>); //参数为null表示(无条件)查询所有

teacherDao.selectBatchIds(List.of(1,3,9)); //根据id的List集合查询

4 条件构造器

条件构造器Wrapper支持各种复杂的where条件

4.1 QueryWrapper

|---------------|---------------------------|---------------------------------------------------------------------------------------------------------|
| 函数名 | 说明 | 示例 |
| .eq | 等于= | .eq("name","张三") |
| .ne | 不等于<> | |
| .gt | 大于> | .gt("age",18) |
| .ge | 大于等于>= | |
| .lt | 小于< | |
| .le | 小于等于<= | |
| .between | BETWEEN值1 AND 值2 #包含值1和值2 | .between("age",18,30) |
| .notBetween | NOT BETWEEN值1 AND 值2 | |
| .like | LIKE'%值%' | .like("name","陈") |
| .likeLeft | LIKE'%值' | |
| .likeRight | LIKE'值%' | |
| .notLike | NOT LIKE'%值%' | |
| .notLikeLeft | NOT LIKE'%值' | |
| .notLikeRight | NOT LIKE'值%' | |
| .isNull | 字段IS NULL | .isNull("name") |
| .isNotNull | 字段IS NOT NULL | |
| .in | 字段IN(v1,v2...) | .in("age",{1,2,3}) |
| .notIn | 字段NOT IN(v1,v2...) | .notIn("age",1,2,3) |
| .inSql | 字段IN(sql语句) | .inSql("id","select id from table where id<3")**==>**id IN (select id from table where id<3) |
| .notInSql | 字段NOT IN(sql语句) | |
| .groupBy | 分组GROUP BY 字段1,字段2... | .groupBy("id","name") |
| .orderByAsc | 排序ORDER BY 字段1,字段2...ASC | .orderByAsc("id","name") |
| .orderByDesc | 排序ORDER BY 字段1,字段2...DESC | |
| .having | 分组后筛选 | .having("CHAR_LENGTH(name)>{0} and age>{1}", 2, 18) |
| .or | 或OR | 不调用or,则默认使用and eq("naem","张三").or().eq("naem","李四") |
| .apply | 拼接sql | 方式一:(有sql注入问题) .apply("sql1"+变量1+"sql2"+常量+"sql3") 方式二:(不会有sql注入问题) .apply("sql1+{0}+sql2+{1}",变量1,变量2) |
| .last | 在最后面拼接sql | |
| .exists | 与EXISTS(子查询)作用相同 | |
| .notExist | 与NOT EXISTS(子查询)作用相同 | |

示例1:查询position中带"语文",年龄age大于18的"position,name,age"字段

java 复制代码
/*SELECT position, name, age 
FROM teacher 
WHERE
	position LIKE '%语文%' 
	AND age > 18;*/
QueryWrapper<Teacher> wrapper = new QueryWrapper<Teacher>()
		.select("position", "name", "age")
		.like("position", "语文")
		.gt("age", 18);
List<Teacher> teachers = teacherDao.selectList(wrapper);

示例2:把name为"张三"的Teacher的age修改为33

java 复制代码
/*UPDATE teacher 
SET age = 45 
WHERE
	NAME = '张三';*/
Teacher teacher = new Teacher();
teacher.setAge(33);
QueryWrapper<Teacher> wrapper = new QueryWrapper<Teacher>()
		.eq("name", "张三");
teacherDao.update(teacher, wrapper);
return Result.success();

4.2 UpdateWrapper

UpdateWrapper提供了2种修改记录的方式:

(1) 将修改内容放在pojo类中,UpdateWrapper只做where筛选(用法同QueryWrapper)

(2) 直接UpdateWrapper对象.setSql(sql),替代sql中的set

示例3:将所有语文老师的年龄增加1岁

java 复制代码
/*UPDATE teacher 
SET age = age + 1 
WHERE
	position = '语文老师';*/
UpdateWrapper<Teacher> wrapper = new UpdateWrapper<Teacher>()
		.setSql("age=age+1")
		.eq("position", "语文老师");
teacherDao.update(wrapper);

4.3 AbstractLambdaWrapper(推荐)

QueryWrapper和UpdateWrapper在写字段时存在硬编码问题,而LambdaQueryWrapper和LambdaUpdateWrapper就可以解决这个问题

LambdaQueryWrapper替换QueryWrapper:查询position中带"语文",年龄age大于18的"position,name,age"字段

java 复制代码
/*SELECT position, name, age 
FROM teacher 
WHERE
	position LIKE '%语文%' 
	AND age > 18;*/
LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<Teacher>()
		.select(Teacher::getPosition, Teacher::getName, Teacher::getAge)
		.like(Teacher::getPosition, "语文")
		.gt(Teacher::getAge, 18);
List<Teacher> teachers = teacherDao.selectList(wrapper);

LambdaUpdateWrapper替换UpdateWrapper:将所有语文老师的年龄增加1岁

java 复制代码
/*UPDATE teacher 
SET age = age + 1 
WHERE
	position = '语文老师';*/
LambdaUpdateWrapper<Teacher> wrapper = new LambdaUpdateWrapper<Teacher>()
		.setSql("age=age+1")
		.eq(Teacher::getPosition, "语文老师");
teacherDao.update(wrapper);

4.4 自定义sql方法

✖问题1:Mybatis只有Dao接口,通过xml映射在SpringIOC中生成Dao实例,因此Wrapper只能写在Service层,导致sql语句无法被其它Service复用

✖问题2:条件构造器善于处理where后面的sql,但不善于处理where前的sql(例如:聚合查询,查询字段起别名...),所以只能将sql写在业务层中(如"示例3"),但实际开发中并不建议在业务层写sql

✔解决:用"where前交给xml文件处理,where后交给MP条件构造器处理"方式解决

4.4.1 dao接口+xml

同mybatis一样,在dao接口中自定义sql方法(此方法与xml文件能映射),方法参数是@Param("ew ")Wrapper和自定义参数

xml文件中用#{自定义参数},${ew.customerSqlSegment}引用

4.4.2 用法示例

示例1:查询position中带"语文",年龄age大于18的"position,name,age"字段

java 复制代码
//dao接口
public interface TeacherDao extends BaseMapper<Teacher> {
    List<Teacher> customSQL1(@Param("ew") LambdaQueryWrapper wrapper);
}

//xml文件
<mapper namespace="xyz.aboluo.mybatisPlus.dao.TeacherDao">
    <resultMap id="teacherMap" type="teacher">
        <result column="position" property="position"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
    </resultMap>

    <select id="customSQL1" resultMap="teacherMap">
        select * from teacher ${ew.customSqlSegment}
    </select>
</mapper>

//使用"自定义sql"
@RequestMapping("/customSQL1.do")
public Result customSQL1() {
	LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<Teacher>()
			.like(Teacher::getPosition, "英语")
			.gt(Teacher::getAge, 18);
	List<Teacher> teachers = teacherDao.customSQL1(wrapper);
	return Result.success(teachers);
}

示例2:把name为"张三"的Teacher的age修改为33

java 复制代码
//dao接口
public interface TeacherDao extends BaseMapper<Teacher> {
    Integer customSQL2(@Param("ew") LambdaQueryWrapper wrapper,  @Param("teacher") Teacher teacher);
}

//xml文件
<mapper namespace="xyz.aboluo.mybatisPlus.dao.TeacherDao">
    <resultMap id="teacherMap" type="teacher">
        <result column="position" property="position"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
    </resultMap>

    <update id="customSQL2" parameterType="teacher">
        update teacher set age=#{teacher.age} ${ew.customSqlSegment}
    </update>
</mapper>

//使用"自定义sql"
@RequestMapping("/customSQL2.do")
public Result customSQL2() {
	Teacher teacher = new Teacher();
	teacher.setAge(33);
	LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<Teacher>()
			.eq(Teacher::getName, "张三");
	teacherDao.customSQL2(wrapper, teacher);
	return Result.success();
}

//使用BaseMapper方法
@RequestMapping("/customSQL3.do")
public Result customSQL3() {
	LambdaUpdateWrapper<Teacher> wrapper = new LambdaUpdateWrapper<Teacher>()
			.set(Teacher::getAge,33)
			.eq(Teacher::getName, "张三");
	teacherDao.update(wrapper);
	return Result.success();
}

5 IService接口

BaseMapper接口是增强Dao接口的,dao接口直接继承BaseMap"增删改查"等方法

IService接口是增强Service接口的,Service接口直接继承IService"增删改查"等方法

5.1 Iservice常用方法

|--------|------------------------------------------------------------------------|-------------------|
| 类型 | 方法 | 说明 |
| 增 | save(T) | |
| 增 | saveBatch(Collection<T>) | |
| 增 | saveOrUpdate(T) | |
| 增 | saveOrUpdate(T,Wrapper<T>) | |
| 增 | saveOrUpdateBatch(Collection<T>) | |
| 删 | remove(Wrapper<T>) | |
| 删 | removeById(T) | |
| 删 | removeBatchByIds(Collection<?>) | |
| 改 | update(Wrapper<T>) | |
| 改 | update(T,Wrapper<T>) | |
| 改 | updateById(T) | |
| 改 | updateBatchById(Collectiion<T>) | |
| 查 | getById(Serializable) //参数是泛型T,并且T继承Serializable,并且用@TableId标记出那个字段是ID | 查询一个用get开头 |
| 查 | getOne(Wrapper<T>) | |
| 查 | listByIds(Collection<? extends Serializable>) | 查询多个用list开头 |
| 查 | list(Wrapper<T>) | 参数null,表示查所有 |
| 查 | lambdaQuery(T) | 参数null,表示查所有 |
| 查 | lambdaUpdate() | |
| 其它 | count(Wrapper<T>) | 统计(参数null,表示统计所有) |
| 其它 | page(E) | 分页查询 |
| 其它 | page(E,Wrapper<T>) | |

5.2 使用流程

因为dao接口是通过xml文件在SpringIOC中反射生成实现类,dao接口直接继承BaseMapper就可以用了,因此不需要写实现类

继承Iservice接口的xxxService接口有xxxServiceImpl实现类,并且实现类需要重写xxxService接口方法和Iservice接口方法,但在实际开发中我们只想重写自定义的xxxService接口,不想重写Iservice接口方法,xxxServiceImpl可以继承MP提供的ServiceImpl方法

5.3 用法示例

(1) xxxService继承IService接口要写pojo泛型

(2) xxxServiceImpl继承ServiceImpl要写dao泛型和pojo泛型

java 复制代码
//xxxService
public interface TeacherService extends IService<Teacher> {
}

//xxxServiceImpl
@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> implements TeacherService {
}

//查询position中带"语文",年龄大于18的"position,name,age"字段
@RequestMapping("/service3.do")
public Result service3() {
    LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<Teacher>()
            .select(Teacher::getPosition, Teacher::getName, Teacher::getAge)
            .like(Teacher::getPosition, "语文")
            .gt(Teacher::getAge, 18);
    return Result.success(teacherService.list(wrapper));
}

//把name为"张三"的Teacher的age修改为33(方式一)
@RequestMapping("/service4.do")
public Result service4() {
    Teacher teacher = new Teacher();
    teacher.setAge(33);
    LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<Teacher>()
            .eq(Teacher::getName, "张三");
    teacherService.update(teacher, wrapper);
    return Result.success();
}

//把name为"张三"的Teacher的age修改为33(方式二)
@RequestMapping("/service5.do")
public Result service5() {
    LambdaUpdateWrapper<Teacher> wrapper = new LambdaUpdateWrapper<Teacher>()
            .set(Teacher::getAge, 33)
            .eq(Teacher::getName, "张三");
    teacherService.update(wrapper);
    return Result.success();
}

5.4 lambdaQuery()方法、lambdaUpdate()方法

这2个方法通常用于复杂条件的查询

lambdaQuery().one表示查一条记录**.list查多条记录.page** 分页查询**.count**统计

lambdaUpdate().set 表示设置字段值**.update**表示执行lambdaUpdate更新

lambdaQuery()示例:根据"position模糊"查询,"name精确"查询,"大于age"查询,并且position,name,age3个字段允许为null,当为null时对此字段不做筛选

java 复制代码
//xxxService
public interface TeacherService extends IService<Teacher> {
    Result lambdaQuery1(Teacher teacher);
}

//xxxServiceImpl
@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> implements TeacherService {
    @Override
    public Result lambdaQuery1(Teacher teacher) {
        List<Teacher> teachers = lambdaQuery()
                //第一个参数condition表示判断,如果为true则执行这一步筛选
                .like(teacher.getPosition() != null, Teacher::getPosition, teacher.getPosition())
                .eq(teacher.getName() != null, Teacher::getName, teacher.getName())
                .gt(teacher.getAge() != null, Teacher::getAge, teacher.getAge())
                .list();
        return Result.success(teachers);
    }
}

//使用
@RequestMapping("/lambdaQuery1.do")
public Result lambdaQuery1(@RequestBody Teacher teacher) {
    return Result.success(teacherService.lambdaQuery1(teacher));
}

lambdaUpdate()示例:将name为"张三"的用户年龄-200岁,如果年龄不足则操作失败

java 复制代码
//xxxDao
public interface TeacherService extends IService<Teacher> {
    Result lambdaQuery2();
}

//xxxDaoImpl
@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> implements TeacherService {
    @Override
    public Result lambdaQuery2() {
        //去数据库查询name为"张三"的Teacher
        LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<Teacher>()
                .eq(Teacher::getName, "张三");
        Teacher teacher = getOne(wrapper);
        Integer restMoney = teacher.getAge() - 200;
        //修改余额
        lambdaUpdate()
                .set(restMoney >= 0, Teacher::getAge, restMoney)
                .set(restMoney < 0, Teacher::getAge, teacher.getAge())//总有一条生效,防止都不生效导致sql错误
                .eq(Teacher::getName, "张三")
                .eq(Teacher::getAge, teacher.getAge())//此处是为了防止并发冲突(之前查到的age要和现在set操作时的age相同)
                .update();
        return Result.success();
    }
}

//使用
@RequestMapping("/lambdaQuery2.do")
public Result lambdaQuery2() {
    teacherService.lambdaQuery2();
    return Result.success();
}

5.5 批量操作

5.5.1 批量操作对比

(1) 使用for循环逐条将sql交给mysql处理(性能极低,每执行一条sql,后台就与mysql通讯一次)

(2) MP基于预编译批量处理(例如xxxDao.saveBatch(list)),将多条sql统一交给(也不能一次给太多,对内存压力大)mysql处理(性能一般,虽然将多条sql统一交给mysql,但mysql依旧是一条条执行)

(3) 将多条sql写作一条sql 例如:多条INSERT INTO table(xx,xx,xx) VALUE (yy,yy,yy) ==> 一条INSERT INTO table(xx,xx,xx) VALUES (yy,yy,yy),(yy,yy,yy)

方式一: 使用手写拼接(不推荐)

方式二: 配置mysql的jdbc驱动(开启rewriteBatchedStatements=true),开启mysql将多条sql整合成一条sql执行

5.5.2 代码示例

例:新增10000条数据,使用"逐条新增"的方式 //耗时28817ms

java 复制代码
@RequestMapping("/batch1.do")
public void batch1() {
    //向数据库逐条插入10000条数据
    for (int i = 0; i < 10000; i++) {
        Teacher teacher = createTeacher(i);
        teacherService.save(teacher);
    }
}

public Teacher createTeacher(Integer positionNo) {
    Teacher teacher = new Teacher();
    teacher.setPosition("" + positionNo);
    return teacher;
}

例:新增10000条数据,使用"预编译"的方式 //耗时3020ms

java 复制代码
@RequestMapping("/batch2.do")
public void batch2() {
    long time1 = System.currentTimeMillis();
    //每次向数据库插入1000条(一次插入过多对内存要求压力大),插入10次
    for (int i = 1; i <= 10000; i++) {
        teachers.add(createTeacher(i));
        if (i % 1000 == 0) {
            teacherService.saveBatch(teachers);
            teachers.clear();
        }
    }
}

public Teacher createTeacher(Integer positionNo) {
    Teacher teacher = new Teacher();
    teacher.setPosition("" + positionNo);
    return teacher;
}

例:新增10000条数据,使用"预编译"+"sql整合"的方式 //耗时1284ms

java 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/sunner?rewriteBatchedStatements=true

6 多表查询

(使用.xml文件)

相关推荐
是程序喵呀几秒前
MySQL备份
android·mysql·adb
Yan.love2 分钟前
开发场景中Java 集合的最佳选择
java·数据结构·链表
椰椰椰耶5 分钟前
【文档搜索引擎】搜索模块的完整实现
java·搜索引擎
大G哥5 分钟前
java提高正则处理效率
java·开发语言
指尖上跳动的旋律8 分钟前
shell脚本定义特殊字符导致执行mysql文件错误的问题
数据库·mysql
一勺菠萝丶19 分钟前
MongoDB 常用操作指南(Docker 环境下)
数据库·mongodb·docker
智慧老师36 分钟前
Spring基础分析13-Spring Security框架
java·后端·spring
lxyzcm37 分钟前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
lucky_syq1 小时前
Hive SQL和Spark SQL的区别?
hive·sql·spark
m0_748244831 小时前
StarRocks 排查单副本表
大数据·数据库·python