阅读 139

MyBatisPlus入门学习

目录

  • MyBatisPlus

    • 插入

    • 主键生成策略

    • 更新操作

    • 自动填充

    • 乐观锁

    • 查询操作

    • 分页查询

    • 删除操作

    • 逻辑删除

    • 性能分析插件

    • 条件构造器

    • 代码自动生成器

    • 概述

    • 快速入门

    • 配置日志输出

    • CRUD拓展


MyBatisPlus

概述

官网:https://baomidou.com/

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初始化,导入依赖)

    <!--数据库驱动--><dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId></dependency><!--lombok--><dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId></dependency><!--mybatis-plus--><!--mybatis-plus是自己开发的,并非官方的--><dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version></dependency>

    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<User> {    //所有的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<User> 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<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));    // System.out.println(users);
    users.forEach(System.out::println);
}//按条件查询 Map@Testpublic void testSelectByMap(){
    HashMap<String,Object> map = new HashMap();    //自定义查询
    map.put("name","周周zzzz");

    List<User> 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<User> 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<String, Object> 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<User> 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<User> wrapper = new QueryWrapper<>();
        wrapper
                .isNotNull("name")
                .isNotNull("email")
                .ge("age",12);
        userMapper.selectList(wrapper).forEach(System.out::println);
    }
}

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

@Testvoid test2(){    //查询名字为周周zzzz的用户
    QueryWrapper<User> 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<User> wrapper = new QueryWrapper<>();
    wrapper.between("age",10,20);
    userMapper.selectCount(wrapper);  //查询结果数}

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

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

测试五:子查询

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

测试六:通过 id 降序排序

@Testvoid test6(){    // 通过 id 降序排序
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.orderByDesc("id");
    List<User> 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<TableFill> 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();
    }
}

本文项目结构目录:

来源:https://www.cnblogs.com/zhou-zr/p/14698200.html

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐