[兔子私房课]MybatisPlus开发详解与项目实战01

简介: 一分钟了解Mybatis-plus

目录


一分钟了解Mybatis-plus


特性


学会Mybatis-plus,我们能简化哪些操作


快速入门


1. 引入依赖


2. 数据准备


3. 配置文件


4. Bean


5. Mapper


6. 启动类


7. 测试类


开启SQL日志


为什么UserMapper继承了BaseMapper就有了selectList方法?


SQL语句是什么时候拼接的?


一分钟了解Mybatis-plus


大家好,我是兔子老师。那么从今天开始呢,就由我来带领大家来了解一下Mybatis-plus这个框架。

为什么他叫Mybatis-plus呢,它和Mybatis又有什么关系?

https://baomidou.com/ 这是官网。

5de1353dbcdc788c56b31c8576e3f217.png

为简化而生,对Mybatis只做增强,不做改变。


MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。


Mybatis-plus的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。


为什么要学习Mybatis-plus呢?说白了,为了偷懒。


怎么个偷懒法呢?我们来看一下特性。


特性

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


损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求


支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题


支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )


内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用


内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询


分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库


内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询


内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作


每一项都能大大地帮我们减轻开发的压力,用起来确实是无比丝滑。现在SPringBoot+MybatisPlus几乎已经是中小型公司开发项目的标配。


因此,我们学好MybatisPlus,不仅仅是学会了一门新的框架,对我们的就业也是有着极大的帮助。


学会Mybatis-plus,我们能简化哪些操作

1.通过继承BaseMapper,自动获取单表CRUD方法,是动态,不需要我们写具体的CRUD实现,也不需要XML配置。以后我们不需要用Mybatis逆向生成工具了,减少了一大堆XML,也不需要通用Mapper了。


2.自带分页插件,以后我们就不需要写PageHelper代码了,引入了MP,就自动拥有了分页功能。


3.全局拦截插件,能帮助我们设置一些字段的默认值,比如Create Time,updateTime等,还可以自己做一些特殊化的拦截配置,防止误操作。


4.强大的代码生成器,自动帮我们生成Ctroller,Service,Dao,怎一个爽子了得,进一步解放我们的双手。


5.QueryWrapper强大的条件控制器,即使是复杂的SQL也不再话下


快速入门


好了,接下来,我会带领大家来完成一个MP的快速入门。方便我们直观地感受一下,MybatisPlus是如何应用到实战中的。

新建springboot项目,版本2.6.13

1. 引入依赖

<!-- 引入Mybatis-plus依赖 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.1.tmp</version>
</dependency>

2. 数据准备

其对应的数据库 Schema 脚本如下:

DROP TABLE IF EXISTS user;
CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);

其对应的数据库 Data 脚本如下:

DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

3. 配置文件

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/mptest?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

我们只需要配置一下数据库连接,其他全都用默认的就行。

4. Bean

@Data
@ToString
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

5. Mapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

6. 启动类

@SpringBootApplication
public class MpdemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpdemoApplication.class, args);
    }
}

默认就行,啥也不需要修改。

7. 测试类

@SpringBootTest
@Slf4j
class MpdemoApplicationTests {
    @Autowired
    UserMapper userMapper;
    @Test
    void searchUsers() {
        log.info("测试MP框架的查询功能");
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

结果:

7c57b4ea45506587106f2ea0c7438dc1.png

思考两个问题?

1.为什么UserMapper继承了BaseMapper就有了selectList方法

2.SQL语句是什么时候拼接的?

开启SQL日志

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

添加上述配置即可。

900d65e90f49527efdb8f40fbbe0c59f.png

就有日志了。

为什么UserMapper继承了BaseMapper就有了selectList方法?

要回答第一个问题,让我们回过头来看看UserMapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

这是一个接口,我们并没有写任何方法,那么只可能是在父类里面。

9f049b89fa74b2fe093bbeafa0827cc9.png

b91046181ec932813c20b4f14137a6b0.png


确实找到了方法,可是BaseMapper依然是个接口,实现逻辑究竟在什么地方呢?


我们试着思考一下,假如你是框架的设计者,会怎么来完成这个壮举?


对于User表来说,基本的CRUD无非是:


1.insert into user values (xxx)


2.delete from user where xxx


3.update user set column = value where xxx


4.select * from user


BaseMapper里面有增删改查方法,对于单表的CRUD操作,我们需要知道表名+字段名即可。


而UserMapper里面是有一个泛型的,对应User类。

dbb09590f065008cf89774303944a24c.png


@Data
@ToString
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}


而MabtisPlus也确实是这么做的,现在我们可以回答为什么UserMapper继承了BaseMapper就有了selectList方法?


大概方向就是框架根据Mapper获取到了对应的泛型类,然后通过反射从泛型类读取了表信息和字段信息,再通过动态代理给UserMapper做动态代理,注入一些列增删改查的方法。当你下次写userMapper.selectList(null);的时候,就会生成动态代理对象,执行对应的方法。

SQL语句是什么时候拼接的?


然后是第二个问题,SQL语句的拼接,这个很重要,为了知道MP框架具体是怎么做的,我们有必要深入源码看一下。

找到MP框架自动配置的地方。

a453ae9e771aee0c346a391c60a1e7e4.png


点进去,找到这个方法:

dbe8ea9bdad067a98105fac0084399e4.png

往下拉,找到这里

// TODO 注入sql注入器
this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);

ISqlInjector就是SQL注入的接口,它有唯一的实现类:AbstractSqlInjector

031dbebd561dc31a807796ea1e851424.png

实现了inspectInject方法,即SQL自动注入

8623692cf193b8afb250b3e856506fa7.png

核心就在于:

methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));

m就是AbstractMethod集合,意思循环遍历方法对象,调用每一个方法对象的inject方法,去注入对应的SQL。这个SQL所需要的参数分别为:


builderAssistant: 一个工具类


mapperClass: Mapper类


ModelClass:Mapper的泛型类


tableInfo:表信息,也是从ModelClass而来


因此,我们之前的推测是完全正确的。


methodList在上面的代码中可以得到,如下图:

List<AbstractMethod> methodList = this.getMethodList(mapperClass);

点进getMethodList

 /**
     * <p>
     * 获取 注入的方法
     * </p>
     *
     * @param mapperClass 当前mapper
     * @return 注入的方法集合
     * @since 3.1.2 add  mapperClass
     */
    public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass);

getMethodList是一个抽象方法,交给子类DefaultSqlInjector去实现的。

@Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        return Stream.of(
            new Insert(),
            new Delete(),
            new DeleteByMap(),
            new DeleteById(),
            new DeleteBatchByIds(),
            new Update(),
            new UpdateById(),
            new SelectById(),
            new SelectBatchByIds(),
            new SelectByMap(),
            new SelectOne(),
            new SelectCount(),
            new SelectMaps(),
            new SelectMapsPage(),
            new SelectObjs(),
            new SelectList(),
            new SelectPage()
        ).collect(toList());
    }

看到这里,恍然大悟,原来BaseMapper里面的方法,都有一个类去专门实现的。AbstractMethod方法拥有17个子类。

e437ac26bc617039e138aba050ce6cc6.png

因此,看了源码我们就知道,不管你是哪个Mapper,这行代码都将得到17个Method类的集合。

39e1e5ba79660e0e9e9cbfd5d8f98fa7.png


让我们把注意力再次放到循环注入自定义方法这块

methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));

点进inject方法

    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        this.configuration = builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
        /* 注入自定义方法 */
        injectMappedStatement(mapperClass, modelClass, tableInfo);
    }

这是AbstractMethod的inject方法,作用是注入自定义方法,然后最后一行的injectMappedStatement又是一个抽象方法,这是模板方法模式。

9a4ace01e2d9cda34e439eed965fe2dc.png

这才是真正注入SQL的方法,AbstractMethod的17个子类,各有各的实现

cac9ca0180c7ef7dceceee149d842dc2.png


让我们不忘初心,就来看一个SelectList类的实现吧。

    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
        String sql = String.format(sqlMethod.getSql(), sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),
            sqlWhereEntityWrapper(true, tableInfo), sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
    }

ff558b3e00283304e716b1dc524874b7.png


现在我们可以回答第二个问题了,SQL语句是什么时候拼接的?

以UserMapper为例,就是:

UserMapper -> User.class -> 获取17个方法类对象(SelectList等) -> 循环注入具体SQL

相关文章
|
7月前
|
Java 测试技术 数据库连接
【MyBatisPlus】MyBatisPlus 整合开发
【1月更文挑战第19天】【MyBatisPlus】MyBatisPlus 整合开发
|
2月前
|
前端开发 Java 数据库连接
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
本文是一份全面的表白墙/留言墙项目教程,使用SpringBoot + MyBatis技术栈和MySQL数据库开发,涵盖了项目前后端开发、数据库配置、代码实现和运行的详细步骤。
65 0
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
3月前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
4月前
|
SQL Java 数据库连接
Spring Boot联手MyBatis,打造开发利器:从入门到精通,实战教程带你飞越编程高峰!
【8月更文挑战第29天】Spring Boot与MyBatis分别是Java快速开发和持久层框架的优秀代表。本文通过整合Spring Boot与MyBatis,展示了如何在项目中添加相关依赖、配置数据源及MyBatis,并通过实战示例介绍了实体类、Mapper接口及Controller的创建过程。通过本文,你将学会如何利用这两款工具提高开发效率,实现数据的增删查改等复杂操作,为实际项目开发提供有力支持。
233 0
|
4月前
|
SQL Java 数据库连接
后端框架的学习----mybatis框架(7、使用注解开发)
这篇文章讲述了如何使用MyBatis框架的注解方式进行开发,包括在接口上使用注解定义SQL语句,并通过动态代理实现对数据库的增删改查操作,同时强调了接口需要在核心配置文件中注册绑定。
|
6月前
|
SQL XML Java
后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操
后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操
75 3
|
6月前
|
Java 数据库连接 API
后端开发之用Mybatis简化JDBC的开发快速入门2024及数据库连接池技术和lombok工具详解
后端开发之用Mybatis简化JDBC的开发快速入门2024及数据库连接池技术和lombok工具详解
68 3
|
6月前
|
SQL Java 数据库连接
2万字实操案例之在Springboot框架下基于注解用Mybatis开发实现基础操作MySQL之预编译SQL主键返回增删改查
2万字实操案例之在Springboot框架下基于注解用Mybatis开发实现基础操作MySQL之预编译SQL主键返回增删改查
81 2
|
6月前
|
SQL Java 数据库连接
Mybatis如何使用mapper代理开发
Mybatis如何使用mapper代理开发
|
7月前
|
SQL Java 关系型数据库
基于SpringBoot使用MyBatisPlus,MyBatisPlus标准数据层开发(CRUD)、MyBatisPlus分页功能的使用
基于SpringBoot使用MyBatisPlus,MyBatisPlus标准数据层开发(CRUD)、MyBatisPlus分页功能的使用
64 2