13-MybatisPlus-入门手册【2021年3月15日 22:13:57 至 2021年3月18日 00:31:36】

单体框架总纲

https://www.jianshu.com/p/0a4a1ced23c7

1、简介

官网:https://mp.baomidou.com/
1、MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生
2、只做增强不做改变,引入它不会对现有工程产生影响
3、只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间。
4、热加载、代码生成、分页、性能分析等功能一应俱全。

目录结构:


image.png

2、创建 User表测试

1、建表语句

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

2、插入数据

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、初始化Springboot项目

1、引入maven 依赖
注意:引入 MyBatis-Plus 之后请不要再次引入 MyBatis,以避免因版本差异导致的问题

 <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>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>

        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--lombok用来简化实体类-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

4、配置编写

1、spring boot 2.0(内置jdbc5驱动)

#mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=a1b2c3

2、spring boot 2.1及以上(内置jdbc8驱动)
注意:driver和url的变化

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=a1b2c3

3、细节说明:

  1. 这里的 url 使用了 ?serverTimezone=GMT%2B8 后缀,因为8.0版本的jdbc驱动需要添加这个后缀,否则运行测试用例报告如下错误:
    java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more
  2. 这里的 driver-class-name 使用了 com.mysql.cj.jdbc.Driver ,在 jdbc 8 中 建议使用这个驱动,否则运行测试用例的时候会有 WARN 信息

5、代码编写

1、启动类编写:在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹

@SpringBootApplication
@MapperScan("com.kk.mybatispulstest.mapper")
public class MybatisPulsTestApplication {
    public static void main(String[] args) {
        SpringApplication.run (MybatisPulsTestApplication.class, args);
    }
}

2、对应实体类

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

3、添加 mapper:创建包 mapper 编写Mapper 接口: UserMapper.java

@Repository //防止找不到这个Bean
public interface UserMapper extends BaseMapper<User> {

}

4、编写测试类:查询出所有

@SpringBootTest
class MybatisPulsTestApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void findAll(){
        // 查询所有测试
        List<User> users = userMapper.selectList (null);
        System.out.println (users );
    }
}

5、查看 sql 日志配置

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

6、主键策略

1、插入操作

    // 添加
    @Test
    public void add(){
        User user = new User ( );
        user.setName ("my_kk");
        user.setAge (27);
        user.setEmail ("123@qq.com");
        int insert = userMapper.insert (user);
        System.out.println ("成功插入条数:"+insert );
    }

2、MP主键策略
(1)ASSIGN_ID:MyBatis-Plus默认的主键策略是:ASSIGN_ID (使用了雪花算法),

@TableId(type = IdType.ASSIGN_ID)
private String id;

(2)雪花算法:分布式ID生成器,由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。

(3)雪花算法核心思想:

  1. 长度共64bit(一个long型)。
  2. 首先是一个符号位,1bit标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0。
  3. 41bit时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年。
  4. 10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID,可以部署在1024个节点)。
  5. 12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。


    image.png

    优点:整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率较高。

3、AUTO 自增策略
(1)需要在创建数据表的时候设置主键自增,实体配置:

@TableId(type = IdType.AUTO)
private Long id;

(2)要想影响所有实体的配置,可以设置全局主键配置

#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto

4、取新增后的 ID

   // 添加
    @Test
    public void add() {
        User user = new User ( );
        user.setName ("my_kk_主键返回测试");
        user.setAge (27);
        user.setEmail ("123@qq.com");
        int insert = userMapper.insert (user);

        System.out.println ("成功插入条数:" + insert);
        System.out.println ("返回的id:" + user.getId ());
    }

7、自动填充和乐观锁

1、自动填充

(1)传统更新操作:

   @Test
    public void update() {
        User user = new User ( );
        user.setId (1371477785738010626L);
        user.setName ("lucymary");
        int count = userMapper.updateById (user);
        System.out.println (count);
    }
(2)自动填充:项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作

<1>填充步骤一:在实体类上加自动填充的注解

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;  // 数据库对应的字段(驼峰) create_time

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime; // 数据库对应的字段(驼峰) update_time

<2>填充步骤二:在实现对象处理接口后,重写进行配置自动填充

public class MyMetaObjectHandler implements MetaObjectHandler {

    // 以mp执行添加操作后,这个方法执行
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName ("createTime", new Date ( ), metaObject);
        this.setFieldValByName ("updateTime", new Date ( ), metaObject);
    }

    // 以mp执行修改操作后,这个方法执行
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}
2、乐观锁

<1>主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新

<2>乐观锁实现方式:

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果version不对,就更新失败

8、乐观锁实现具体步骤:

1、数据库创建字段,并在实体类对应的属性加上注解

    @Version
    private Integer version; // 数据库对应的字段: version
    @TableField(value="version", fill = FieldFill.INSERT, update="%s+1")
     private Integer version; // 数据库对应的字段: version

2、创建配置文件,并注册乐观锁插件

@Configuration
@MapperScan("com.kk.mybatispulstest.mapper")
public class MybatisPlusConfig {
    /**
     * 乐观锁插件
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor ( );
    }
}

9、查询

1、通过多个 id 批量查询

    // 批量查询
    @Test
    public void testSelect1() {
        List<User> users = userMapper.selectBatchIds (Arrays.asList (1, 2, 3));
        System.out.println (users);
    }

2、简单的条件查询:通过map封装查询条件
注意:map中的key对应数据库中的列名。如:数据库user_id,实体类是userId,这时map的key需要填写user_id

    // 简单条件查询
    @Test
    public void testSelect2() {
        HashMap<String, Object> columnMap = new HashMap<> ( );
        columnMap.put ("name","jack");
        columnMap.put ("age",20);
        // 同时满足以上两个条件
        List<User> users = userMapper.selectByMap (columnMap);
        System.out.println (users );
    }

10、分页

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

1、添加分页插件:MybatisPlusConfig 中

    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor ( );
    }

2、测试selectPage分页

 //分页查询
    @Test
    public void testSelectPage() {
        Page<User> page = new Page<> (1, 3);
        Page<User> userPage = userMapper.selectPage (page, null);

        // 返回对象得到分页所有数据
        long pages = userPage.getPages ( );              // 总页数
        long current = userPage.getCurrent ( );          // 当前页
        List<User> records = userPage.getRecords ( );    // 查询数据集合
        long total = userPage.getTotal ( );              // 总记录数
        boolean hasNext = userPage.hasNext ( );          // 下一页
        boolean hasPrevious = userPage.hasPrevious ( );  // 上一页
    }

3、测试selectPage分页:返回结果集为 map
当指定了特定的查询列时,希望分页结果列表只返回被查询的列,而不是很多null值(值为 null 的 字段不会被封装进 map内 )

    @Test
    public void testSelectMapsPage() {
        //Page不需要泛型
        Page<Map<String, Object>> page = new Page<> (1, 5);
        Page<Map<String, Object>> pageParam = userMapper.selectMapsPage (page, null);
        List<Map<String, Object>> records = pageParam.getRecords ( );
        records.forEach (System.out::println);
        System.out.println (pageParam.getCurrent ( ));
        System.out.println (pageParam.getPages ( ));
        System.out.println (pageParam.getSize ( ));
        System.out.println (pageParam.getTotal ( ));
        System.out.println (pageParam.hasNext ( ));
        System.out.println (pageParam.hasPrevious ( ));
    }

11、删除

1、根据 id 删除

    @Test
    public void testDeleteById(){
        int i = userMapper.deleteById (1371477785738010626L);
        System.out.println (i );
    }

2、根据 id 批量删除

    @Test
    public void testDeleteBatchIds() {
        int result = userMapper.deleteBatchIds (Arrays.asList (8, 9, 10));
        System.out.println (result);
    }

3、简单条件删除

    @Test
    public void testDeleteByMap() {
        HashMap<String, Object> map = new HashMap<> ( );
        map.put ("name", "Helen");
        map.put ("age", 18);
        int result = userMapper.deleteByMap (map);
        System.out.println (result);
    }

12、逻辑删除

1、物理删除和逻辑删除

  1. 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
  2. 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
  3. 逻辑删除的使用场景:可以进行数据恢复,有关联数据,不便删除

2、具体实现步骤:
(1)数据库添加字段:

ALTER TABLE `user` ADD COLUMN `deleted` boolean DEFAULT false

(2)对应实体属性加注解

    @TableLogic
    private Integer deleted;

(3)配置(可选)
application.properties mp中默认的配置,如果不想改变一下可以不用写

mybatis-plus.global-config.db-config.logic-delete-value=1 #删除
mybatis-plus.global-config.db-config.logic-not-delete-value=0

(4)测试:

    @Test
    public void testLogicDelete() {
        int result = userMapper.deleteById (1L);
        User user = userMapper.selectById (1L);
        System.out.println (result);
        System.out.println ("查找删除后的"+user );// 输出结果:查找删除后的null
    }
  1. 测试后发现,数据并没有被删除,deleted字段的值由0变成了1
  2. 测试后分析打印的sql语句,是一条update
  3. 注意:被删除前,数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作

(5)测试删除后的查询:MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断

@Test
    public void testLogicDeleteSelect() {
        List<User> users = userMapper.selectList (null);
        users.forEach (System.out::println);
    }

13、条件构造器和常用接口

1、wapper介绍


image.png
Wrapper : 条件构造抽象类,最顶端父类  
    AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
        QueryWrapper : 查询条件封装
        UpdateWrapper : Update 条件封装
    AbstractLambdaWrapper : 使用Lambda 语法
        LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
        LambdaUpdateWrapper : Lambda 更新封装Wrapper

2、案例
(1)案例一:ge、gt、le、lt、isNull、isNotNull

    @Test
    public void testQuery() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<> ( );
        queryWrapper.isNull ("name")// name 等于 null
                .ge ("age",12)  // 并且 age > 12
                .isNotNull ("email");// 并且 email 不等于 null
        int delete = userMapper.delete (queryWrapper);
        System.out.println ("被删除的记录数:"+delete );
    }

(2)案例二:eq、ne
注意:seletOne()返回的是一条实体记录,当出现多条时会报错

    @Test
    public void testSelectOne() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<> ( );
        queryWrapper.eq ("name", "tom");
        // 只能返回一条记录,多余一条则抛出异常
        User user = userMapper.selectOne (queryWrapper);
        System.out.println (user);
    }

(3)案例三:between、notBetween,区间包含

    @Test
    public void testSelectCount() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<> ( );
        queryWrapper.between ("age", 20, 30);// 年纪 20 到 30 岁
        Integer count = userMapper.selectCount (queryWrapper);
        List<User> users = userMapper.selectList (queryWrapper);
        System.out.println ("符合个数:" + count);
        System.out.println ("符合的集合:" + users);
    }

(4)案例四: like、notLike、likeLeft、likeRight
selectMaps()返回Map集合列表,通常配合select()使用

    @Test
    public void testSelectMaps() {
        QueryWrapper<User> qw = new QueryWrapper<> ( );
        qw.select ("name", "age")// 指定返回的字段
                .like ("name", "a")// 模糊搜索
                .likeRight ("email", "4");// 模糊向右
        // 以 map形式返回
        List<Map<String, Object>> maps = userMapper.selectMaps (qw);
        maps.forEach (System.out::println);
    }

(5)案例五:orderBy、orderByDesc、orderByAsc

    @Test
    public void testSelectListOrderBy() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<> ( );
        // 先根据年龄降序,再根据id
        queryWrapper.orderByDesc ("age", "id");
        List<User> users = userMapper.selectList (queryWrapper);
        users.forEach (System.out::println);
    }

3、查询方式

查询方式 说明
select 设置 SELECT 查询字段
where WHERE 语句,拼接 + WHERE 条件
and AND 语句拼接 + AND 字段=值
andNew AND 语句,拼接 + AND (字段=值)
or OR 语句,拼接 + OR 字段=值
orNew OR 语句,拼接 + OR (字段=值)
eq 等于=
allEq 基于 map 内容等于=
ne 不等于<>
gt 大于>
ge 大于等于>=
lt 小于<
le 小于等于<=
like 模糊查询 LIKE
notLike 模糊查询 NOT LIKE
in IN 查询
notIn NOT IN 查询
isNull NULL 值查询
isNotNull IS NOT NULL
groupBy 分组 GROUP BY
having HAVING 关键词
orderBy 排序 ORDER BY
orderAsc ASC 排序 ORDER BY
orderDesc DESC 排序 ORDER BY
exists EXISTS 条件语句
notExists NOT EXISTS 条件语句
between BETWEEN 条件语句
notBetween NOT BETWEEN 条件语句
addFilter 自由拼接 SQL
last 拼接在最后,例如:last(“LIMIT 1”)

参考:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/89482201

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容