MyBatisPlus入门学习
发布日期:2021-05-09 09:24:36 浏览次数:17 分类:博客文章

本文共 14310 字,大约阅读时间需要 47 分钟。

目录

MyBatisPlus

概述

官网:

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

MyBatisPlus可以节省我们大量的工作时间,所有的CRUD可以自动化完成。

JPA,tk-mapper,MyBatisPlus。

特性:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

快速入门

使用第三方插件:

  1. 导入对应的依赖
  2. 研究依赖如何配置
  3. 编写代码,拓展

步骤

  1. 创建数据库,mybaits_plus

  2. 创建user表

    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));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');--真实开发中,version(乐观锁),deleted(逻辑删除),gmt_create,gmt_modified
  3. 初始化项目(使用SpringBoot初始化,导入依赖)

    mysql
    mysql-connector-java
    org.projectlombok
    lombok
    com.baomidou
    mybatis-plus-boot-starter
    3.0.5

    mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus!避免版本的差异!

  4. 连接数据库,和mybatis相同。

    application.properties

    # mysql5spring.datasource.username=rootspring.datasource.password=123456spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&useSSL=falsespring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#mysql8 驱动不同com.mysql.cj.jdbc.Driver(可兼容mysql5),需要配置时区 serverTimezone=GMT%2B8
  5. 传统方式:pojo-dao(连接mybatis,配置mapper.xml)-service-controller

  6. 使用mybatis-plus后,pojo-mapper接口-使用

    pojo

    package com.zr.pojo;@Data@AllArgsConstructor@NoArgsConstructorpublic class User {    private Long id;    private String name;    private Integer age;    private String email;}

    mapper接口

    package com.zr.mapper;//在对应的mapper上实现基本的类 BaseMapper@Repository //代表持久层的public interface UserMapper extends BaseMapper
    { //所有的CRUD操作已经编写完成了 //不需要配置其它文件了}

    主启动类

    package com.zr;//扫描mapper文件夹@MapperScan("com.zr.mapper")@SpringBootApplicationpublic class MybatisPlusApplication {    public static void main(String[] args) {        SpringApplication.run(MybatisPlusApplication.class, args);    }}

    测试

    package com.zr;@SpringBootTestclass MybatisPlusApplicationTests {    //继承了BaseMapper,所有的方法都来自于父类    //我们也可以编写自己的拓展方法    @Autowired    private UserMapper userMapper;    @Test    void contextLoads() {        //参数是一个 Wrapper,条件构造器,这里先不用 null        //查询全部用户        List
    users = userMapper.selectList(null); users.forEach(System.out::println); }}

注意点:主启动类上扫描mapper下的所有接口。

配置日志输出

我们所有的sql现在是不可见的,我们希望知道它是怎么执行的,就必须看日志。

application.properties中增加

#配置日志mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

日志输出结果:

CRUD拓展

插入

package com.zr;@SpringBootTestclass MybatisPlusApplicationTests {    //继承了BaseMapper,所有的方法都来自于父类    //我们也可以编写自己的拓展方法    @Autowired    private UserMapper userMapper;    //测试插入    @Test    public void testInsert(){        User user = new User();        user.setName("周周");        user.setAge(20);        user.setEmail("813794474@qq.com");        userMapper.insert(user);  //自动生成ID        System.out.println(user.toString());    }}

测试结果:

数据库插入ID的默认值为:全局唯一的ID。

主键生成策略

可查询分布式系统唯一id生成解决方案。

雪花算法:SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法(long型的ID)。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id,其中41bit作为毫秒数,10bit作为机器的id(5bit是数据中心,5bit是机器id),12bit作为毫秒内的流水号(意味着每个节点在每秒可产生4096个ID),最后有一位符号位,永远是0。在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的。

主键配置:

字段上加 @TableId(type = IdType.AUTO)

数据库字段一定要是递增。

package com.zr.pojo;@Data@AllArgsConstructor@NoArgsConstructorpublic class User {    //对应数据库中的主键(uuid,自增id,雪花算法,redis,zookeeper)    @TableId(type = IdType.AUTO)    private Long id;    private String name;    private Integer age;    private String email;}

测试插入。

源码:

public enum IdType {    AUTO(0), //数据库id自增    NONE(1),  //未设置主键    INPUT(2),  //手动输入    ID_WORKER(3),  //默认的全局唯一id    UUID(4),  //全局唯一id    ID_WORKER_STR(5);  // ID_WORKER的字符串表示法}

更新操作

测试方法中添加:

//更新@Testpublic void testUpdate(){    User user = new User();    user.setId(22L);    user.setName("周周zzzz");    user.setAge(20);    userMapper.updateById(user);}

sql自动动态配置

自动填充

创建时间,修改时间!这些操作都是自动化完成的,我们不希望手动更新。

阿里巴巴开发手册:所有的数据库,gmt_creat,gmt-modified几乎所有的表都要配置上,而且需要自动化。

方式一:数据库级别(工作中不允许修改数据库的,不建议使用)

  1. 在表中新增字段,create_time,update_time(create_time不勾选根据当前时间更新)

  2. 再次测试插入方法,需要先把实体类同步!

    private Date createTime;private Date updateTime;
  3. 再次测试更新操作。

方式二:代码级别

  1. 删除时间的默认值

  2. 实体类字段属性上需要增加注解

    //字段插入填充内容@TableField(fill = FieldFill.INSERT)private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;
  3. 编写处理器来处理注解

    package com.zr.handler;@Slf4j@Component  //加到IOC容器中public class MyMetaObjectHandler implements MetaObjectHandler {    //插入时的填充策略    @Override    public void insertFill(MetaObject metaObject) {        log.debug("start insert....");        this.setFieldValByName("createTime",new Date(),metaObject);        this.setFieldValByName("updateTime",new Date(),metaObject);    }    //更新时的填充策略    @Override    public void updateFill(MetaObject metaObject) {        log.debug("start update....");        this.setFieldValByName("updateTime",new Date(),metaObject);    }}
  4. 测试插入,观察时间更新。

乐观锁

乐观锁:顾名思义非常乐观,总是认为不会出现问题,无论干什么都不去上锁,如果出现了问题,再次更新值测试。

悲观锁:顾名思义非常悲观,总是认为会出现问题,无论干什么都加锁,再去操作。

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败
乐观锁,先查询,获取版本号---Aupdate user set name = "zhour",version=version+1where id = 2 and version = 1---B 线程先完成,这个时候versioon=2,A执行失败update user set name = "zhour",version=version+1where id = 2 and version = 1

测试乐观锁MP插件:

数据库中增加version字段

同步实体类

//乐观锁的注解@Versionprivate Integer version;

注册组件(主启动类的扫描mapper放到这里)MyBatisPlusConfig

package com.zr.config;//扫描mapper文件夹@MapperScan("com.zr.mapper")@EnableTransactionManagement@Configuration //代表是一个配置类public class MyBatisPlusConfig {    //注册乐观锁插件    @Bean    public OptimisticLockerInterceptor optimisticLockerInterceptor(){        return new OptimisticLockerInterceptor();    }}

测试:添加以下代码

//测试乐观锁成功@Testpublic void testOptimisticLocker(){    //线程1    //查询用户信息    User user = userMapper.selectById(222L);    //修改用户信息    user.setName("zhourrr");    user.setEmail("813794474@qq.com");    //执行更新操作    userMapper.updateById(user);}//测试乐观锁失败,多线程下@Testpublic void testOptimisticLocker2(){    //线程1    User user1 = userMapper.selectById(222L);    user1.setName("zhourrr111");    user1.setEmail("813794474@qq.com");    //模拟另外一个线程执行了插队操作    User user2 = userMapper.selectById(222L);    user2.setName("zhourrr222");    user2.setEmail("813794474@qq.com");    userMapper.updateById(user2);    //自旋锁来多次尝试提交    userMapper.updateById(user1); //如果没有乐观锁就会覆盖插队线程的值}

查询操作

//测试批量查询@Testpublic void testSelectById(){    User user = userMapper.selectById(222L);    System.out.println(user);}@Testpublic void testSelectBatchIds(){    List
users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); // System.out.println(users); users.forEach(System.out::println);}//按条件查询 Map@Testpublic void testSelectByMap(){ HashMap
map = new HashMap(); //自定义查询 map.put("name","周周zzzz"); List
users = userMapper.selectByMap(map); users.forEach(System.out::println);}

分页查询

分页在网站的使用非常多。

  1. 原始用limit分页
  2. pageHelper 第三方插件
  3. MP内置了分页插件

使用:

  1. 配置拦截器组即可(官网示例)

    MyBatisPlusConfig中添加

    //分页插件    @Bean    public PaginationInterceptor paginationInterceptor() {        return new PaginationInterceptor;    }
  2. 直接使用page对象即可

    //测试分页查询@Testpublic void testPage(){    //参数1:当前页    //参数2:页面的大小    Page
    page = new Page<>(1,5); userMapper.selectPage(page,null); page.getRecords().forEach(System.out::println); System.out.println(page.getTotal());}

删除操作

根据id删除

//测试删除@Testpublic void testDeleteById(){    userMapper.deleteById(1342445919922073602L);}//通过id批量删除@Testpublic void testDeleteBatchByIds(){    userMapper.deleteBatchIds(Arrays.asList(1342445919922073602L,222));}//通过Map删除@Testpublic void testDeleteByMap(){    HashMap
map = new HashMap<>(); map.put("name","zhourrr222"); userMapper.deleteByMap(map);}

逻辑删除

物理删除:从数据库中直接移除

逻辑删除:在数据库中没有被移除,而是通过一个变量让它失效

应用:管理员可以查看被删除的内容,防止数据的丢失,类似于回收站!

测试:

  1. 在数据库中增加一个deleted字段

  2. 实体类中增加属性

    @TableLogic  //逻辑删除注解private Integer deleted;
  3. 配置 MyBatisPlusConfig

    //逻辑删除组件@Beanpublic ISqlInjector sqlInjector(){    return new LogicSqlInjector();}

    application.properties中添加

    #配置逻辑删除# 逻辑已删除值(默认为 1)# 逻辑未删除值(默认为 0)mybatis-plus.global-config.db-config.logic-delete-value=1mybatis-plus.global-config.db-config.logic-not-delete-value=0
  4. 测试

观察数据库中id为111的用户deleted的值变为1.

再次查询id为111的用户,显示为空(只会查询deleted值为0的用户)。

性能分析插件

在开发中,我们会遇到一些慢sql。测试,durid....

MP也提供了性能分析插件,如果超过这个时间就停止运行。

  1. 导入插件

    //sql执行效率插件@Bean@Profile({"dev","test"})  //设置dev,test环境开启public PerformanceInterceptor performanceInterceptor(){    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();    performanceInterceptor.setMaxTime(100); //设置sql能够执行的最大时间    performanceInterceptor.setFormat(true); //是否格式化sql语句    return performanceInterceptor;}

    在application.properties中配置环境为测试或者开发环境。

    #设置开发环境spring.profiles.active=dev
  2. 测试使用

    @Testvoid contextLoads() {    //参数是一个 Wrapper,条件构造器,这里先不用 null    //查询全部用户    List
    users = userMapper.selectList(null); users.forEach(System.out::println);}

sql执行的时间和格式化的sql

执行的时间超过了设定的时间就会抛出异常。

使用性能分析插件可以帮助我们提高效率!

条件构造器

AbstractWrapper.

我们写一些复杂的sql就可以使用它来替代。

新建一个测试类 WrapperTest。

以下的测试结合日志的 SQL 语句来分析。

测试一:查询name不为空的用户,并且邮箱也不为空,且年龄大于12的用户

package com.zr;@SpringBootTestpublic class WrapperTest {    @Autowired    private UserMapper userMapper;    @Test    void contextLoads() {        //查询name不为空的用户,并且邮箱也不为空,且年龄大于12的用户        QueryWrapper
wrapper = new QueryWrapper<>(); wrapper .isNotNull("name") .isNotNull("email") .ge("age",12); userMapper.selectList(wrapper).forEach(System.out::println); }}

测试二:查询名字为周周zzzz的用户

@Testvoid test2(){    //查询名字为周周zzzz的用户    QueryWrapper
wrapper = new QueryWrapper<>(); wrapper.eq("name","周周zzzz"); User user = userMapper.selectOne(wrapper); //查询一个数据,查询多个用list或者map System.out.println(user);}

测试三:查询年龄在10-20之间的用户

@Testvoid test3(){    //查询年龄在10-20之间的用户    QueryWrapper
wrapper = new QueryWrapper<>(); wrapper.between("age",10,20); userMapper.selectCount(wrapper); //查询结果数}

测试四:查询名字中没有字母e,且邮箱是以t开头的(t%)

@Testvoid test4(){    //模糊查询    QueryWrapper
wrapper = new QueryWrapper<>(); wrapper .notLike("name","e") .likeRight("email","t"); List
> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println);}

测试五:子查询

@Testvoid test5(){    // id 在子查询中查出来    QueryWrapper
wrapper = new QueryWrapper<>(); wrapper.inSql("id","select id from user where id < 3"); List
objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println);}

测试六:通过 id 降序排序

@Testvoid test6(){    // 通过 id 降序排序    QueryWrapper
wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); List
users = userMapper.selectList(wrapper); users.forEach(System.out::println);}

其它更多测试参考mybatis-plus官方文档。

代码自动生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

package com.zr;public class ZrCode {    public static void main(String[] args) {        //代码自动生成        AutoGenerator mpg = new AutoGenerator();        //全局配置        GlobalConfig gc = new GlobalConfig();        String projectPath = System.getProperty("user.dir");        gc.setOutputDir(projectPath+"/src/main/java");        gc.setAuthor("zhour");        gc.setOpen(false);        gc.setFileOverride(false); //是否覆盖        gc.setServiceName("%sService");  //去Service的 i 前缀        gc.setIdType(IdType.ID_WORKER);        gc.setDateType(DateType.ONLY_DATE);        gc.setSwagger2(true);        mpg.setGlobalConfig(gc);        //数据源配置        DataSourceConfig ds = new DataSourceConfig();        ds.setUrl("jdbc:mysql://localhost:3306/mybaits_plus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT");        ds.setDriverName("com.mysql.cj.jdbc.Driver");        ds.setUsername("root");        ds.setPassword("123456");        ds.setDbType(DbType.MYSQL);        mpg.setDataSource(ds);        //包的配置        PackageConfig pc = new PackageConfig();        pc.setModuleName("blog");        pc.setParent("com.zrcode");        pc.setEntity("entity");        pc.setMapper("mapper");        pc.setController("controller");        pc.setService("service");        mpg.setPackageInfo(pc);        //策略配置        StrategyConfig strategy = new StrategyConfig();        strategy.setInclude("user");  //设置要映射的表名        strategy.setNaming(NamingStrategy.underline_to_camel); //转驼峰命名        strategy.setColumnNaming(NamingStrategy.underline_to_camel); //转驼峰命名        strategy.setEntityLombokModel(true);        strategy.setLogicDeleteFieldName("deleted");  //逻辑删除        //自动填充        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);        ArrayList
list = new ArrayList<>(); list.add(createTime); list.add(updateTime); strategy.setTableFillList(list); //乐观锁 strategy.setVersionFieldName("version"); strategy.setRestControllerStyle(true); strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_3,下划线命名 mpg.setStrategy(strategy); mpg.execute(); }}

本文项目结构目录:

上一篇:Docker学习笔记---通俗易懂
下一篇:Redis学习笔记

发表评论

最新留言

不错!
[***.144.177.141]2025年05月05日 04时35分55秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

#C8# UVM中的factory机制 #S8.1.1# OOP 语言三大特性 systemverilog的支持 2025-03-28
#C8# UVM中的factory机制 #S8.1.4# 约束的重载 2025-03-28
#C8# UVM中的factory机制 #S8.2.3# 重载sequence哪些情形 2025-03-29
#C8# UVM中的factory机制 #S8.4.1# factory机制的实现 2025-03-29
900行c语言贪吃蛇,原生js实现的贪吃蛇网页版游戏完整实例 2025-03-29
ado读取多条oracle数据,Oracle ADO数据存取 2025-03-29
android fastjson漏洞_初识Fastjson漏洞(环境搭建及漏洞复现) 2025-03-29
asp.mvc 4项目发布文件目录结构_如何用SpringBoot(2.3.3版本)快速搭建一个项目?文末有小彩蛋... 2025-03-29
aspen串联反应怎么输入_如何进步提升串联谐振试验装置的稳定性 2025-03-29
c++ string取子串_Integer与String的设计哲学 2025-03-29
c++ 数组批量赋值_数组之间不能赋值?穿个马甲吧! 2025-03-29
cad模糊查询符号_mysql 正则模式和like模糊查询 2025-03-29
continue可以用if判断里面吗_谁能说说if()else()里的continue是干嘛的? 2025-03-29
ctrl c 和 ctrl v 不能用了_神奇操作,原来CTRL键还能这么用 2025-03-29
cytoscape安装java_Cytoscape史上最全攻略 2025-03-29
c语言程序设计年历显示,C语言程序设计报告《万年历》.doc 2025-03-29
C语言程序设计梁海英答案,1.5 习题 2025-03-29
c语言编写单片机中断,C语言AVR单片机中断程序写法 2025-03-29
ddr2的上电顺序_S5PV210 DDR2初始化 28个步骤总结 2025-03-29
embedding层_【预估排序】Embedding+MLP: 深度学习预估排序通用框架(一) 2025-03-29