ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

MybatisPlus简单使用

2021-02-20 14:01:36  阅读:186  来源: 互联网

标签:MybatisPlus name 简单 wrapper 查询 version user 使用 ID


MyBatis-Plus

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

特性:

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

  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

测试使用MP:

引入依赖:本次测试SpringBoot版本为2.2.2

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--lombok用来简化实体类-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

准备数据库测试数据:

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)
);
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');

配置连接参数:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
# 配置mybatis plus日志输出
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

测试连接:

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() throws SQLException {
        // null,查询所有
        List<User> users = userMapper.selectList(null);
        System.out.println(users);
    }

在这里插入图片描述

测试添加用户:

在这里插入图片描述

可以发现MP会自动生成全局唯一的id。

分布式系统唯一ID生成方案:详见:博客园

1、数据库自增

2、UUID、UUID变种

3、Redis(单线程,原子操作)

4、SnowFlake(雪花算法)

5、Zookeeper(znode数据版本号)

6、MongDB(ObjectId)

MP自带的策略分析:

tips:mybatis-plus从3.3.0开始,默认使用雪花算法+UUID(没有-)

描述
AUTO数据库ID自增
NONE无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUTinsert前自行set主键值
ASSIGN_ID分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUID分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)
ID_WORKER(X分布式全局唯一ID 长整型类型(please use ASSIGN_ID)
UUID(X32位UUID字符串(please use ASSIGN_UUID)
ID_WORKER_STR(X分布式全局唯一ID 字符串类型(please use ASSIGN_ID)

项目中经常会遇到一些数据,每次都使用相同的方式进行添加,例如创建/更新时间,这时候我们可以使用MP中的自动填充功能。

对于需要自动填充的字段:

    // 插入自动填充字段
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    // 修改自动填充字段
    @TableField(fill = FieldFill.UPDATE)
    private Date updateTime;
    // 插入和修改自动填充字段
    // @TableField(fill = FieldFill.INSERT_UPDATE)

编写实现类:

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 我这里使用的版本比较旧,新版本使用对应的方法即可
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("*******start insert fill");
        this.setFieldValByName("createTime", new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("*******start update fill");
        this.setFieldValByName("updateTime", new Date(),metaObject);
    }
}

测试修改数据:修改时自动设置updateTime

在这里插入图片描述
丢失更新(并发修改同一条记录,导致后提交的事务覆盖了之前的事务修改的数据)
解决方案:乐观锁、悲观锁。
这里介绍乐观锁:
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新。
实现方式:取出记录时,获取当前version,更新时带上这个version,执行更新时判断version是否改变,如果version不对就更新失败。
测试使用乐观锁:
1、为数据库中的表添加一个保存version的字段。

ALTER TABLE `user` ADD COLUMN `version` INT

2、为实体类的成员变量添加注解

    @Version
    @TableField(fill = FieldFill.INSERT)
    private Integer version;

3、注入乐观锁插件(拦截器)

    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor(){
        return new OptimisticLockerInterceptor();
    }

4、测试乐观锁

        // 取出记录时,带上version
        User user = userMapper.selectById(1362942307736760322L);
        user.setAge(110);

        // 更新时,对比version
        userMapper.updateById(user);

可以发现修改成功后version自动加1了。
在这里插入图片描述
乐观锁修改失败:

        // 取出记录时,带上version
        User user = userMapper.selectById(1362942307736760322L);
        user.setAge(999);
        user.setVersion(user.getVersion() + 1); // 模拟已经被其他线程修改了数据

        // 更新时,对比version
        userMapper.updateById(user);

测试批量id查询和简单条件查询:

    @Test
    void testBatch(){
        List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        userList.forEach(System.out::println);

        Map<String,Object> map = new HashMap<>();
        map.put("name","zhangsan");
        map.put("age",110);
        // name=zhangsan AND age=110
        userMapper.selectByMap(map);
    }

注入分页插件:

    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }

测试分页查询:

    void testPage(){
        Page<User> page = new Page<>(1,3); // 当前页,记录数

        userMapper.selectPage(page, null);// 分页查询

        System.out.println("当前页:"+page.getCurrent());
        System.out.println("当前页所有数据:"+page.getRecords());
        System.out.println("每页显示记录数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有下一页:"+page.hasNext());
        System.out.println("是否有上一页:"+page.hasPrevious());
    }

物理删除(真实删除):将对应数据从数据库中删除,之后查询不到这条数据
逻辑删除(假删除):将对应数据中代表删除状态修改为被删除状态,之后在数据库中仍能看到这条数据。
数据库添加deleted字段:

ALTER TABLE `user` ADD COLUMN `deleted` boolean

在成员属性上添加注解:

    @TableLogic // 逻辑删除状态
    @TableField(fill = FieldFill.INSERT)
    private Integer deleted;

注入逻辑删除插件:

    @Bean
    public ISqlInjector iSqlInjector(){
        return new LogicSqlInjector();
    }

测试删除数据:

userMapper.deleteById(1L);

在这里插入图片描述
发现只是将该条记录中的deleted字段修改为1。
测试查询该条数据:
在这里插入图片描述
被逻辑删除的数据也是查询不到的。
性能分析插件:用于输出每条SQL语句及执行时间,开发环境使用,超过指定时间,停止运行,有助于发现问题。
注入性能分析插件:

    @Bean
    @Profile(value = {"dev","test"}) // 开发、测试环境生效
    public PerformanceInterceptor performanceInterceptor(){
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();

        performanceInterceptor.setMaxTime(500); // 超时时间,超过不执行sql
        performanceInterceptor.setFormat(false); // 是否格式化

        return performanceInterceptor;
    }

测试:
在这里插入图片描述
复杂条件查询:更加复杂的条件查询需要使用wrapper(条件构造器)
Wrapper : 条件构造抽象类,最顶端父类

  • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
    • QueryWrapper : Entity 对象封装操作类,不是用lambda语法
    • UpdateWrapper : Update 条件封装,用于Entity对象更新操作
  • AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
    • LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
    • LambdaUpdateWrapper : Lambda 更新封装Wrapper
    /**
     * 测试QueryWrapper基本使用
     */
    @Test
    void testWrapper() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // ge(>=)、gt(>)、le(<=)、lt(<)、isNull、isNotNull
//        wrapper.ge("age",10).gt("version",1)
//                .le("deleted",10).lt("create_time",new Date())
//                .isNotNull("name");
        // eq(=)、ne(≠)
//        wrapper.eq("name","zhangsan").eq("age",110)
//                .ne("deleted",1);
        // (包含边界)between、notBetween
//        wrapper.between("age",110,120)
//                .notBetween("version",1,2);
        // allEq(每个字段的值都相等)
//        Map<String,Object> map = new HashMap<>();
//        map.put("name","zhangsan");
//        map.put("age",110);
//        map.put("version",3);
//
//        wrapper.allEq(map);
        // like、notLike、likeLeft、likeRight
//        wrapper.like("name","zhangsan") // %zhangsan%
//                .likeRight("email","lisi"); // lisi%
        // in、notIn、inSql(子查询)、notInSql、exists、notExists
//        wrapper.in("age",Collections.singletonList(110))
//                .inSql("id","select id from user where id>3");
        // or、and
        // name=lisi OR age=110,不使用or就默认使用and
//        wrapper.eq("name","lisi").or().eq("age",110);
        // 嵌套or、嵌套and
        // name=lisi OR(age=110 AND version=3)
//        wrapper.eq("name","lisi")
//                .or(i->i.eq("age",110).eq("version",3));
        // orderBy、OrderByDesc(降序)、OrderByAsc(升序)
//        wrapper.orderByDesc("id");
//        List<User> users = userMapper.selectList(wrapper);
//        System.out.println(users);
        // last,直接拼接在sql的最后面,有sql注入风险,并且只能使用一次,以最后一次为准
//        wrapper.last("limit 1");
        // 指定要查询的列
//        wrapper.select("name","age","version");
        // set、setSql
//        UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>();
//        updateWrapper
//                .like("name", "zhang")
//                .set("name", "lisi") // 查询并设置值
//                .setSql("email='lisi@qq.com'"); // 可以有子查询
        // UPDATE user SET age=?, update_time=?, name=?, email = 'lisi@qq.com' WHERE deleted=0 AND name LIKE ?

        User user = userMapper.selectOne(wrapper); // 有多个结果会报错
        System.out.println(user);
    }

标签:MybatisPlus,name,简单,wrapper,查询,version,user,使用,ID
来源: https://blog.csdn.net/weixin_44246189/article/details/113864551

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有