MyBatis-Plus【学习记录】

本文详细介绍了MyBatis-Plus的学习过程,包括简介、入门案例、功能实现、条件构造器和常用接口、插件使用,如分页插件和乐观锁,以及通用枚举和代码生成器的使用,最后讲解了如何配置多数据源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mybatis-Plus

一、简介

官方文档:https://baomidou.com/pages/24112f/

二、入门案例

1、开发环境

IDE:idea

JDK:JDK8+

构建工具:maven

数据库:MySQL

SpringBoot

MyBatis-Plus

2、创建数据库以及表

创建数据库表

3、创建Spring Boot工程

a>初始化工程

添加依赖

<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>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

配置数据源

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

创建实体类

package com.kj.mybatisplus.pojo;

import lombok.*;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Long id;

    private String name;

    private Integer age;

    private String email;

}

创建接口

package com.kj.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kj.mybatisplus.pojo.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

测试

package com.kj.mybatisplus;

import com.kj.mybatisplus.mapper.UserMapper;
import com.kj.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class MybatisplusTest {

    @Autowired UserMapper userMapper;

    @Test
    public void test(){
        //通过条件构造器查询一个list集合,若没有条件,则可以设置为null
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
}

添加日志

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password:
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

==>  Preparing: SELECT id,name,age,email FROM user
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, trst2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==      Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@c2e3264]
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)

三、功能

新增

    @Test
    public void testInsert(){

        User user = new User(null,"张三",23,"zhangsan@baomidou.com");
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
        System.out.println("userid:"+user.getId());
    }

/*
==>  Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
==> Parameters: 1643803350057201666(Long), 张三(String), 23(Integer), zhangsan@baomidou.com(String)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@642505c7]
result:1
userid:1643803350057201666
*/

删除

    @Test
    public void testDelete(){
        //通过id删除
        int result = userMapper.deleteById(1643803350057201666L);
        System.out.println("result:"+result);
    }

/*
==>  Preparing: DELETE FROM user WHERE id=?
==> Parameters: 1643803350057201666(Long)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2d9f64c9]
result:1
*/
    @Test
    public void testDelete(){
        //通过map来删除
        Map<String,Object> map = new HashMap<>();
        map.put("name","张三");
        map.put("age",23);
        int result = userMapper.deleteByMap(map);
        System.out.println("result"+result);
    }

/*
==>  Preparing: DELETE FROM user WHERE name = ? AND age = ?
==> Parameters: 张三(String), 23(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4339e0de]
result1
*/
    @Test
    public void testDelete(){
        //通过多个id实现批量删除
        List<Long> list = Arrays.asList(4L, 5L);
        int result = userMapper.deleteBatchIds(list);
        System.out.println("result"+result);
    }

/*
==>  Preparing: DELETE FROM user WHERE id IN ( ? , ? )
==> Parameters: 4(Long), 5(Long)
<==    Updates: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21ac5eb4]
result2
*/

修改

    @Test
    public void testUpdate(){
        //根据id修改用户信息
        User user = new User();
        user.setId(1L);
        user.setName("Lili");
        user.setAge(15);
        int result = userMapper.updateById(user);
        System.out.println("result"+result);
    }

/*
==>  Preparing: UPDATE user SET name=?, age=? WHERE id=?
==> Parameters: Lili(String), 15(Integer), 1(Long)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@61d84e08]
result1
*/

查询

    @Test
    public void testSelect(){
        //根据id查询
        User result = userMapper.selectById(1L);
        System.out.println("result"+result);
    }

/*
==>  Preparing: SELECT id,name,age,email FROM user WHERE id=?
==> Parameters: 1(Long)
<==    Columns: id, name, age, email
<==        Row: 1, Lili, 15, test1@baomidou.com
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@52d6cd34]
resultUser(id=1, name=Lili, age=15, email=test1@baomidou.com)
*/
    @Test
    public void testSelect(){
        //根据多个id查询多个用户信息
        List<Long> list = Arrays.asList(1L, 2L, 3L, 4L);
        List<User> result = userMapper.selectBatchIds(list);
        result.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? , ? )
==> Parameters: 1(Long), 2(Long), 3(Long), 4(Long)
<==    Columns: id, name, age, email
<==        Row: 1, Lili, 15, test1@baomidou.com
<==        Row: 2, Jack, 20, trst2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, 张三, 23, zhangsan@baomidou.com
<==      Total: 4
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6428591a]
User(id=1, name=Lili, age=15, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com)
*/
    @Test
    public void testSelect(){
        //根据map查询
        Map<String,Object> map = new HashMap<>();
        map.put("name","张三");
        map.put("age",23);
        List<User> result = userMapper.selectByMap(map);
        result.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
==> Parameters: 张三(String), 23(Integer)
<==    Columns: id, name, age, email
<==        Row: 4, 张三, 23, zhangsan@baomidou.com
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@c2e3264]
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com)
*/
    @Test
    public void testSelect(){
        //根据id查询
//        User result = userMapper.selectById(1L);
//        System.out.println("result"+result);
        //根据多个id查询多个用户信息
//        List<Long> list = Arrays.asList(1L, 2L, 3L, 4L);
//        List<User> result = userMapper.selectBatchIds(list);
//        result.forEach(System.out::println);
        //根据map查询
//        Map<String,Object> map = new HashMap<>();
//        map.put("name","张三");
//        map.put("age",23);
//        List<User> result = userMapper.selectByMap(map);
//        result.forEach(System.out::println);
        //查询所有数据
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT id,name,age,email FROM user
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Lili, 15, test1@baomidou.com
<==        Row: 2, Jack, 20, trst2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, 张三, 23, zhangsan@baomidou.com
<==        Row: 5, 李四, 24, lisi@baomidou.com
<==      Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@57c47a9e]
User(id=1, name=Lili, age=15, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com)
User(id=5, name=李四, age=24, email=lisi@baomidou.com)
*/

自定义功能

@Mapper
public interface UserMapper extends BaseMapper<User> {

    /**
     * 根据id查询用户信息,返回map
     * @param id
     * @return
     */
    Map<String,Object> selectMapById(Long id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.kj.mybatisplus.mapper.UserMapper">

    <select id="selectMapById" resultType="map">
        select name,age,email from mybatis_plus.user where id = #{id}
    </select>

</mapper>

    @Test
    public void testSelect(){
        //自定义查询
        Map<String, Object> map = userMapper.selectMapById(1L);
        System.out.println(map);
    }

/*
==>  Preparing: select name,age,email from mybatis_plus.user where id = ?
==> Parameters: 1(Long)
<==    Columns: name, age, email
<==        Row: Lili, 15, test1@baomidou.com
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2a685eba]
{name=Lili, age=15, email=test1@baomidou.com}
*/

通用Service

说明:

  • 通用Service CRUD封装Service接口,进一步封装CRUD采用get查询单行,remove删除,list查询集合,page分页,前缀命名方式区分mapper层避免混淆
  • 泛型T为任意实体对象
  • 建议如果存在自定义通用Service方法的可能,请创建自己的IBaseService继承Mybatis_Plus的基类
IService

Mybatis_Plus中有一个接口IService和其实现类ServiceImpl,封装了常见的业务逻辑

详情查看源码 IService 和 ServiceImpl

创建Service接口和实现类

接口

package com.kj.mybatisplus.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.kj.mybatisplus.pojo.User;

public interface UserService extends IService<User> {
}

实现类

package com.kj.mybatisplus.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kj.mybatisplus.mapper.UserMapper;
import com.kj.mybatisplus.pojo.User;
import com.kj.mybatisplus.service.UserService;

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

测试

package com.kj.mybatisplus;

import com.kj.mybatisplus.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MybatisplusServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testGetCount(){
        //查询总记录数
        long count = userService.count();
        System.out.println(count);
    }

}

/*
==>  Preparing: SELECT COUNT( * ) FROM user
==> Parameters: 
<==    Columns: COUNT( * )
<==        Row: 5
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3419e23b]
5
*/
批量添加
    @Test
    public void testInsertMore(){
        //批量添加
        List<User> list = new ArrayList<>();
        for(int i=1;i<=10;i++){
            User user = new User();
            user.setId(5L+i);
            user.setName("abc"+i);
            user.setAge(20+i);
            list.add(user);
        }
        boolean b = userService.saveBatch(list);
        System.out.println(b);
    }

/*
==>  Preparing: INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? )
==> Parameters: 6(Long), abc1(String), 21(Integer)
==> Parameters: 7(Long), abc2(String), 22(Integer)
==> Parameters: 8(Long), abc3(String), 23(Integer)
==> Parameters: 9(Long), abc4(String), 24(Integer)
==> Parameters: 10(Long), abc5(String), 25(Integer)
==> Parameters: 11(Long), abc6(String), 26(Integer)
==> Parameters: 12(Long), abc7(String), 27(Integer)
==> Parameters: 13(Long), abc8(String), 28(Integer)
==> Parameters: 14(Long), abc9(String), 29(Integer)
==> Parameters: 15(Long), abc10(String), 30(Integer)
true
*/

@TableName

操控的表由UserMapper中BaseMapper<泛型(实体类)>设置的泛型(实体类)决定

package com.kj.mybatisplus.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;

@Data
@AllArgsConstructor
@NoArgsConstructor
//设置实体类所对应的表名
@TableName("user")
public class User {

    private Long id;

    private String name;

    private Integer age;

    private String email;

}

如果所有表名由统一的前缀,在yml文件中设置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password:
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      table-prefix: t_

@TableId

将属性所对应的字段指定为主键

@TableId
private Long uid;
value属性

@TableId注解的value属性用于指定主键的字段

例:表中属性名为uid,实体类中名称为id

@TableId(value = "uid")
private Long id;

只有value属性时可写为:

@TableId("uid")
private Long uid;
type属性

@TableId注解的type属性设置主键生成策略

当数据库中属性设置为自增,而Mybatis-Plus默认为雪花算法时

@TableId(value = "uid",type = IdType.AUTO)
private Long id;
通过全局配置主键生成策略
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password:
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      table-prefix: t_
      #设置统一的主键生成策略
      id-type: auto

雪花算法

  • 背景

需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。

数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。

  • 数据库分表

将不同的业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如:淘宝的几亿用户数据,如果全部存放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进行拆分。

单表数据拆分有两种方式:垂直分表和水平分表

  • 垂直分表

垂直分表适合将表中某些不常用且占据了大量空间的列拆分出去。

例如:假设有一个婚恋网站,用户在筛选其它用户时,主要使用的是age和sex两个字段进行查询,而其它字段主要用于展示,一般不会在业务查询中用到,因此我们可以将这两个字段独立到另外一张表中,这样在查询age和sex时,就能带来一定的性能提升。

  • 水平分表

水平分表适合表行数特别大的表,有的公司要求单表行数超过5000万就必须进行分表,这个数字可以作为参考,但并不是绝对标准,关键还是要看表的访问性能。对于一些比较复杂的表,可能超过1000万就要分表了;而对于一些简单的表,即使存储数据超过1亿行,也可以不分表。

但不管怎样,当看到表的数据量达到千万级别时,作为架构师就要警觉起来,因为这很可能是架构的性能瓶颈或者隐患。

水平分表相比垂直分表,会引入更多的复杂性,例如要求全局唯一的数据id该如何处理。

主键自增

  1. 以最常见的用户ID为例,可以按照1000000的范围大小进行分段,1999999放到表1中,10000001999999放到表2中,以此类推。
  2. 复杂点:分段大小的选取。分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会导致单表依然存在性能问题,一般建议分段大小在100万至2000万之间,具体需要根据业务选取合适的分段大小。
  3. 优点:可以随着数据的增加平滑地扩充新的表。例如,现在的用户是100万,如果增加到1000万,只需要增加新的表就可以了,原有的数据不需要动。
  4. 缺点:分布不均匀。假如按照1000万来进行分表,有可能某个分段实际存储的数据量只有一条,而另外一个分段实际存储的数据量有1000万条。

取模

  1. 同样以用户ID为例,假如我们一开始就规划了10个数据库表,可以简单的用user_id % 10的值来表示数据所属的数据库表编号,ID为985的用户放到编号为5的子表中,ID为10086的用户放到标号为6的子表中。
  2. 复杂点:初始表数量的确定。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。
  3. 优点:表分布比较均匀。
  4. 扩充新的表很麻烦,所有的表都要重分布。

雪花算法

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

  1. 核心思想:
    1. 长度共64bit(一个Long型)
    2. 首先是一个符号位,1bit标识,由于Long型在java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
    3. 41bit时间戳(毫秒级),存储的是时间戳的差值(当前时间戳-开始时间戳),结果约等于69.73年
    4. 10bit作为机器的ID(5bit是数据中心,5bit是机器ID,可以部署在1024个节点)
    5. 12bit作为毫秒的流水号(意味着每个节点在每毫秒可以产生4096个ID)
  2. 优点:整体上按照时间自增排序,并且整个分布式系统不会产生ID碰撞,并且效率较高

@TableField

指定属性所对应的字段名

例:数据库中字段名为:user_name,实体类中属性名为:name

    @TableField("user_name")
    private String name;

@TableLogic

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

step1:数据库中创建逻辑删除状态列,设置默认值为0

step2:在实体类中添加isDelete属性,并添加@TableLogic注解

    @TableLogic
    private Integer isDeleted;

step3:运行删除功能

    @Test
    public void testDelete(){
        //通过id删除
//        int result = userMapper.deleteById(1643803350057201666L);
//        System.out.println("result:"+result);
        //通过map来删除
//        Map<String,Object> map = new HashMap<>();
//        map.put("name","张三");
//        map.put("age",23);
//        int result = userMapper.deleteByMap(map);
//        System.out.println("result"+result);
        //通过多个id实现批量删除
        List<Long> list = Arrays.asList(14L,15L);
        int result = userMapper.deleteBatchIds(list);
        System.out.println("result"+result);
    }

/*
==>  Preparing: UPDATE user SET is_deleted=1 WHERE id IN ( ? , ? ) AND is_deleted=0
==> Parameters: 14(Long), 15(Long)
<==    Updates: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7487b142]
result2
*/

step4:运行查询功能

    @Test
    public void testSelect(){
        //根据id查询
//        User result = userMapper.selectById(1L);
//        System.out.println("result"+result);
        //根据多个id查询多个用户信息
//        List<Long> list = Arrays.asList(1L, 2L, 3L, 4L);
//        List<User> result = userMapper.selectBatchIds(list);
//        result.forEach(System.out::println);
        //根据map查询
//        Map<String,Object> map = new HashMap<>();
//        map.put("name","张三");
//        map.put("age",23);
//        List<User> result = userMapper.selectByMap(map);
//        result.forEach(System.out::println);
//        查询所有数据
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
        //自定义查询
//        Map<String, Object> map = userMapper.selectMapById(1L);
//        System.out.println(map);
    }

/*
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0
==> Parameters: 
<==    Columns: id, name, age, email, is_deleted
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 3, Tom, 28, test3@baomidou.com, 0
<==        Row: 4, 张三, 23, zhangsan@baomidou.com, 0
<==        Row: 5, 李四, 24, lisi@baomidou.com, 0
<==        Row: 6, abc1, 21, null, 0
<==        Row: 7, abc2, 22, null, 0
<==        Row: 8, abc3, 23, null, 0
<==        Row: 9, abc4, 24, null, 0
<==        Row: 10, abc5, 25, null, 0
<==        Row: 11, abc6, 26, null, 0
<==        Row: 12, abc7, 27, null, 0
<==        Row: 13, abc8, 28, null, 0
<==        Row: 16, 李四, 24, lisi@baomidou.com, 0
<==      Total: 14
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@790a251b]
User(id=1, name=Lili, age=15, email=test1@baomidou.com, isDeleted=0)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0)
User(id=3, name=Tom, age=28, email=test3@baomidou.com, isDeleted=0)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com, isDeleted=0)
User(id=5, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
User(id=6, name=abc1, age=21, email=null, isDeleted=0)
User(id=7, name=abc2, age=22, email=null, isDeleted=0)
User(id=8, name=abc3, age=23, email=null, isDeleted=0)
User(id=9, name=abc4, age=24, email=null, isDeleted=0)
User(id=10, name=abc5, age=25, email=null, isDeleted=0)
User(id=11, name=abc6, age=26, email=null, isDeleted=0)
User(id=12, name=abc7, age=27, email=null, isDeleted=0)
User(id=13, name=abc8, age=28, email=null, isDeleted=0)
User(id=16, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
*/

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

1、wrapper介绍

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

2、QueryWrapper

组装查询条件

package com.kj.mybatisplus;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kj.mybatisplus.mapper.UserMapper;
import com.kj.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class MybatisplusWrapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void queryWrapperTest(){
        //查询用户名中包含a,年龄在20到30之间,邮箱信息不为空的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","a")
                        .between("age",20,30)
                                .isNotNull("email");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

}

/*
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
==> Parameters: %a%(String), 20(Integer), 30(Integer)
<==    Columns: id, name, age, email, is_deleted
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@56913163]
User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0)
*/

组装排序条件

    @Test
    public void queryWrapperTest02(){
        //查询用户信息,按照年龄降序排序,若年龄相同,按照id升序排序
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("age")
                        .orderByAsc("id");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 ORDER BY age DESC,id ASC
==> Parameters: 
<==    Columns: id, name, age, email, is_deleted
<==        Row: 3, Tom, 28, test3@baomidou.com, 0
<==        Row: 13, abc8, 28, null, 0
<==        Row: 12, abc7, 27, null, 0
<==        Row: 11, abc6, 26, null, 0
<==        Row: 10, abc5, 25, null, 0
<==        Row: 5, 李四, 24, lisi@baomidou.com, 0
<==        Row: 9, abc4, 24, null, 0
<==        Row: 16, 李四, 24, lisi@baomidou.com, 0
<==        Row: 4, 张三, 23, zhangsan@baomidou.com, 0
<==        Row: 8, abc3, 23, null, 0
<==        Row: 7, abc2, 22, null, 0
<==        Row: 6, abc1, 21, null, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==      Total: 14
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@702b06fb]
User(id=3, name=Tom, age=28, email=test3@baomidou.com, isDeleted=0)
User(id=13, name=abc8, age=28, email=null, isDeleted=0)
User(id=12, name=abc7, age=27, email=null, isDeleted=0)
User(id=11, name=abc6, age=26, email=null, isDeleted=0)
User(id=10, name=abc5, age=25, email=null, isDeleted=0)
User(id=5, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
User(id=9, name=abc4, age=24, email=null, isDeleted=0)
User(id=16, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com, isDeleted=0)
User(id=8, name=abc3, age=23, email=null, isDeleted=0)
User(id=7, name=abc2, age=22, email=null, isDeleted=0)
User(id=6, name=abc1, age=21, email=null, isDeleted=0)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0)
User(id=1, name=Lili, age=15, email=test1@baomidou.com, isDeleted=0)
*/

组装删除条件

    @Test
    public void test03(){
        //删除邮箱地址为NULL的信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNull("email");
        int result = userMapper.delete(queryWrapper);
        System.out.println("result:"+result);
    }

/*
==>  Preparing: UPDATE user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)
==> Parameters: 
<==    Updates: 8
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1b9776f5]
result:8
*/
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0
==> Parameters: 
<==    Columns: id, name, age, email, is_deleted
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 3, Tom, 28, test3@baomidou.com, 0
<==        Row: 4, 张三, 23, zhangsan@baomidou.com, 0
<==        Row: 5, 李四, 24, lisi@baomidou.com, 0
<==        Row: 16, 李四, 24, lisi@baomidou.com, 0
<==      Total: 6
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3e15bb06]
User(id=1, name=Lili, age=15, email=test1@baomidou.com, isDeleted=0)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0)
User(id=3, name=Tom, age=28, email=test3@baomidou.com, isDeleted=0)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com, isDeleted=0)
User(id=5, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
User(id=16, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)

3、使用QueryWrapper实现修改功能

    @Test
    public void test04(){
        //修改年龄大于20且用户名包含o或者邮箱地址为空的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age",20)
                .like("name","o")
                .or()
                .isNull("email");
        User user = new User();
        user.setAge(26);
        user.setName("lisa");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("result:"+result);
    }

/*
==>  Preparing: UPDATE user SET name=?, age=? WHERE is_deleted=0 AND (age > ? AND name LIKE ? OR email IS NULL)
==> Parameters: lisa(String), 26(Integer), 20(Integer), %o%(String)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@a18649a]
result:1
*/
条件的优先级
    @Test
    public void test05(){
        //修改用户名包含s且(年龄大于20或邮箱地址为空)的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper();
        queryWrapper.like("name","s")
                .and(i->i.gt("age",20).or().isNull("email"));
        User user = new User();
        user.setName("haha");
        user.setAge(24);
        int result = userMapper.update(user, queryWrapper);
        System.out.println("result:"+result);
    }

/*
==>  Preparing: UPDATE user SET name=?, age=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
==> Parameters: haha(String), 24(Integer), %s%(String), 20(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3d5790ea]
result:1
*/
组装select字句
    @Test
    public void test06(){
        //查询用户的用户名,年龄,邮箱信息
        QueryWrapper<User> queryWrapper = new QueryWrapper();
        queryWrapper.select("name","age","email");
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        maps.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT name,age,email FROM user WHERE is_deleted=0
==> Parameters: 
<==    Columns: name, age, email
<==        Row: Lili, 15, test1@baomidou.com
<==        Row: Jack, 20, trst2@baomidou.com
<==        Row: haha, 24, test3@baomidou.com
<==        Row: 张三, 23, zhangsan@baomidou.com
<==        Row: 李四, 24, lisi@baomidou.com
<==        Row: 李四, 24, lisi@baomidou.com
<==      Total: 6
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a7cc52c]
{name=Lili, age=15, email=test1@baomidou.com}
{name=Jack, age=20, email=trst2@baomidou.com}
{name=haha, age=24, email=test3@baomidou.com}
{name=张三, age=23, email=zhangsan@baomidou.com}
{name=李四, age=24, email=lisi@baomidou.com}
{name=李四, age=24, email=lisi@baomidou.com}
*/
组装子查询
    @Test
    public void test07(){
        //查询id小于等于5的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper();
        queryWrapper.inSql("id","select id from user where id <=5");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (id IN (select id from user where id <=5))
==> Parameters: 
<==    Columns: id, name, age, email, is_deleted
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 3, haha, 24, test3@baomidou.com, 0
<==        Row: 4, 张三, 23, zhangsan@baomidou.com, 0
<==        Row: 5, 李四, 24, lisi@baomidou.com, 0
<==      Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fc142ec]
User(id=1, name=Lili, age=15, email=test1@baomidou.com, isDeleted=0)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0)
User(id=3, name=haha, age=24, email=test3@baomidou.com, isDeleted=0)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com, isDeleted=0)
User(id=5, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
*/

4、使用UpdateWrapper实现修改功能

    @Test
    public void test08(){
        //修改用户名包含h且(年龄大于20或邮箱地址为空)的用户信息
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.like("name","h")
                .and(i -> i.gt("age",20).or().isNull("email"));
        updateWrapper.set("name","nini").set("email","nini@baomidou.com");
        int result = userMapper.update(null, updateWrapper);
        System.out.println("result:"+result);
    }

/*
==>  Preparing: UPDATE user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
==> Parameters: nini(String), nini@baomidou.com(String), %h%(String), 20(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c534b5b]
result:1
*/

5、模拟开发中组装条件的情况

    @Test
    public void test09(){
        String name = "";
        Integer ageBegin = 10;
        Integer ageEnd = 25;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(name)){
        //isNotBlank:判断某个字符串是否不为空字符串,不为null,不为空白符
            queryWrapper.like("name",name);
        }
        if (ageBegin != null){
            queryWrapper.gt("age",ageBegin);
        }
        if (ageEnd != null){
            queryWrapper.le("age",ageEnd);
        }
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (age > ? AND age <= ?)
==> Parameters: 10(Integer), 25(Integer)
<==    Columns: id, name, age, email, is_deleted
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 3, nini, 24, nini@baomidou.com, 0
<==        Row: 4, 张三, 23, zhangsan@baomidou.com, 0
<==        Row: 5, 李四, 24, lisi@baomidou.com, 0
<==        Row: 16, 李四, 24, lisi@baomidou.com, 0
<==      Total: 6
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c534b5b]
User(id=1, name=Lili, age=15, email=test1@baomidou.com, isDeleted=0)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0)
User(id=3, name=nini, age=24, email=nini@baomidou.com, isDeleted=0)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com, isDeleted=0)
User(id=5, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
User(id=16, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
*/
使用condition组装条件
    @Test
    public void test10(){
        String name = "";
        Integer ageBegin = 10;
        Integer ageEnd = 25;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(name),"name",name)
                .gt(ageBegin!=null,"age",ageBegin)
                .le(ageEnd!=null,"age",ageEnd);
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (age > ? AND age <= ?)
==> Parameters: 10(Integer), 25(Integer)
<==    Columns: id, name, age, email, is_deleted
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 3, nini, 24, nini@baomidou.com, 0
<==        Row: 4, 张三, 23, zhangsan@baomidou.com, 0
<==        Row: 5, 李四, 24, lisi@baomidou.com, 0
<==        Row: 16, 李四, 24, lisi@baomidou.com, 0
<==      Total: 6
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2418ba04]
User(id=1, name=Lili, age=15, email=test1@baomidou.com, isDeleted=0)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0)
User(id=3, name=nini, age=24, email=nini@baomidou.com, isDeleted=0)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com, isDeleted=0)
User(id=5, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
User(id=16, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
*/

6、Lambda

LambdaQueryWrapper
    @Test
    public void test11(){
        String name = "";
        Integer ageBegin = 10;
        Integer ageEnd = 25;
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(name),User::getName,name)
                .gt(ageBegin!=null,User::getAge,ageBegin)
                .le(ageEnd!=null,User::getAge,ageEnd);
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

/*
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (age > ? AND age <= ?)
==> Parameters: 10(Integer), 25(Integer)
<==    Columns: id, name, age, email, is_deleted
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 3, nini, 24, nini@baomidou.com, 0
<==        Row: 4, 张三, 23, zhangsan@baomidou.com, 0
<==        Row: 5, 李四, 24, lisi@baomidou.com, 0
<==        Row: 16, 李四, 24, lisi@baomidou.com, 0
<==      Total: 6
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c534b5b]
User(id=1, name=Lili, age=15, email=test1@baomidou.com, isDeleted=0)
User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0)
User(id=3, name=nini, age=24, email=nini@baomidou.com, isDeleted=0)
User(id=4, name=张三, age=23, email=zhangsan@baomidou.com, isDeleted=0)
User(id=5, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
User(id=16, name=李四, age=24, email=lisi@baomidou.com, isDeleted=0)
*/
LambdaUpdateWrapper
    @Test
    public void test12(){
        //修改id为16且(年龄大于20或邮箱地址为空)的用户信息
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(User::getId,16)
                .and(i -> i.gt(User::getAge,20).or().isNull(User::getEmail));
        updateWrapper.set(User::getName,"sasha").set(User::getEmail,"sasha@baomidou.com");
        int result = userMapper.update(null, updateWrapper);
        System.out.println("result:"+result);
    }

/*
==>  Preparing: UPDATE user SET name=?,email=? WHERE is_deleted=0 AND (id = ? AND (age > ? OR email IS NULL))
==> Parameters: sasha(String), sasha@baomidou.com(String), 16(Integer), 20(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3ee69ad8]
result:1
*/

五、插件

1、分页插件

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

添加配置类
package com.kj.mybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.kj.mybatisplus.mapper")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}
测试
package com.kj.mybatisplus;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kj.mybatisplus.mapper.UserMapper;
import com.kj.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MybatisplusPluginTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void pageTest(){
        Page<User> page = new Page<>(1,3);
        userMapper.selectPage(page, null);
        System.out.println(page);
    }
}

/*
==>  Preparing: SELECT COUNT(*) AS total FROM user WHERE is_deleted = 0
==> Parameters: 
<==    Columns: total
<==        Row: 6
<==      Total: 1
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 LIMIT ?
==> Parameters: 3(Long)
<==    Columns: id, name, age, email, is_deleted
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 3, nini, 24, nini@baomidou.com, 0
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3a543f31]
com.baomidou.mybatisplus.extension.plugins.pagination.Page@4601203a
*/
分页相关数据获取
    @Test
    public void pageTest(){
        Page<User> page = new Page<>(1,3);
        userMapper.selectPage(page, null);
        System.out.println(page.getPages());
        System.out.println(page.getRecords());
        System.out.println(page.getCurrent());
        System.out.println(page.getSize());
        System.out.println(page.hasNext()+","+page.hasPrevious());
    }

/*
==>  Preparing: SELECT COUNT(*) AS total FROM user WHERE is_deleted = 0
==> Parameters: 
<==    Columns: total
<==        Row: 6
<==      Total: 1
==>  Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 LIMIT ?
==> Parameters: 3(Long)
<==    Columns: id, name, age, email, is_deleted
<==        Row: 1, Lili, 15, test1@baomidou.com, 0
<==        Row: 2, Jack, 20, trst2@baomidou.com, 0
<==        Row: 3, nini, 24, nini@baomidou.com, 0
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7187bac9]
2
[User(id=1, name=Lili, age=15, email=test1@baomidou.com, isDeleted=0), User(id=2, name=Jack, age=20, email=trst2@baomidou.com, isDeleted=0), User(id=3, name=nini, age=24, email=nini@baomidou.com, isDeleted=0)]
1
3
true,false
*/
自定义分页功能
package com.kj.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kj.mybatisplus.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.Map;

@Mapper
public interface UserMapper extends BaseMapper<User> {

    /**
     * 根据id查询用户信息,返回map
     * @param id
     * @return
     */
    Map<String,Object> selectMapById(Long id);

    /**
     * 根据年龄分页查询用户信息
     * @param page
     * @param age
     * @return
     */
    Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);

}

配置别名

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password:
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  type-aliases-package: com.kj.mybatisplus.pojo
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.kj.mybatisplus.mapper.UserMapper">

    <select id="selectMapById" resultType="map">
        select name,age,email from mybatis_plus.user where id = #{id}
    </select>

    <select id="selectPageVo" resultType="User">
        select id,name,age,email from mybatis_plus.user where age > #{age}
    </select>

</mapper>
    @Test
    public void teatPageVo(){
        Page<User> page = new Page<>(1,3);
        userMapper.selectPageVo(page, 20);
        System.out.println(page);
    }

/*
==>  Preparing: SELECT COUNT(*) AS total FROM mybatis_plus.user WHERE age > ?
==> Parameters: 20(Integer)
<==    Columns: total
<==        Row: 14
<==      Total: 1
==>  Preparing: select id,name,age,email from mybatis_plus.user where age > ? LIMIT ?
==> Parameters: 20(Integer), 3(Long)
<==    Columns: id, name, age, email
<==        Row: 3, nini, 24, nini@baomidou.com
<==        Row: 4, 张三, 23, zhangsan@baomidou.com
<==        Row: 5, 李四, 24, lisi@baomidou.com
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7efe7b87]
com.baomidou.mybatisplus.extension.plugins.pagination.Page@1937eaff
*/

2、乐观锁

场景

一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把价格降低30元。

此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李价格加了50元,并将100+50=150元存入了数据库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就完全被小王覆盖了。

现在商品价格是70元,比成本低10元。几分钟后,这个商品很快出售了1千多件,老板亏1万多。

乐观锁与悲观锁

上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被修改了。如果被修改过了,则重新取出被修改后的价格,150元,这样他会将120元存入数据库。

如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证最终的价格是120元。

模拟修改冲突
数据库中增加商品表
create table product
(
  `id` BIGINT(20) NOT NULL COMMENT'主键ID',
	`name` VARCHAR(30) NULL DEFAULT NULL COMMENT'商品名称',
	`price` INT(11) DEFAULT 0 COMMENT'价格',
	`VERSION` INT(11) DEFAULT 0 COMMENT'乐观锁版本号',
	PRIMARY KEY(id)
);
INSERT INTO product(id,name,price) VALUES(1,'外星人笔记本',100);
添加实体类
package com.kj.mybatisplus.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("product")
public class Product {

    private Long id;

    private String name;

    private Integer price;

    private Integer version;
}
添加Mapper
package com.kj.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kj.mybatisplus.pojo.Product;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
模拟冲突
    @Test
    public void testProduct01(){
        //小李查询价格
        Product productL = productMapper.selectById(1);
        System.out.println("小李查询到的价格为:"+productL.getPrice());
        //小王查询价格
        Product productW = productMapper.selectById(1);
        System.out.println("小王查询到的价格为:"+productW.getPrice());
        //小李修改价格
        productL.setPrice(productL.getPrice()+50);
        productMapper.updateById(productL);
        //小王修改价格
        productW.setPrice(productW.getPrice()-30);
        productMapper.updateById(productW);
        //老板查询价格
        Product productB = productMapper.selectById(1);
        System.out.println("老板查询到的价格为:"+productB.getPrice());
    }

/*
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 100, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@15dc339f]
小李查询到的价格为:100
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7544ac86] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@992457879 wrapping com.mysql.cj.jdbc.ConnectionImpl@2630dbc4] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 100, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7544ac86]
小王查询到的价格为:100
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512d6e60] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@859985937 wrapping com.mysql.cj.jdbc.ConnectionImpl@2630dbc4] will not be managed by Spring
==>  Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=?
==> Parameters: 外星人笔记本(String), 150(Integer), 0(Integer), 1(Long)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512d6e60]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1caa9eb6] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@525551643 wrapping com.mysql.cj.jdbc.ConnectionImpl@2630dbc4] will not be managed by Spring
==>  Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=?
==> Parameters: 外星人笔记本(String), 70(Integer), 0(Integer), 1(Long)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1caa9eb6]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27e7c77f] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1869652507 wrapping com.mysql.cj.jdbc.ConnectionImpl@2630dbc4] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 70, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27e7c77f]
老板查询到的价格为:70
*/
乐观锁实现流程

数据库中添加version字段

取出记录时,获取当前version

SELECT `id`,`name`,`price`,`version` FROM product WHERE id=1

更新时,version+1,如果WHERE语句中的version版本不对,则更新失败

UPDATE prodect SET price=price+50,`version`=`version`+1 WHERE id=1 AND `version`=1
MyBatisPlus实现乐观锁
修改实体类
package com.kj.mybatisplus.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("product")
public class Product {

    private Long id;

    private String name;

    private Integer price;

    @Version //添加乐观锁注解
    private Integer version;
}
添加乐观锁插件
package com.kj.mybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.kj.mybatisplus.mapper")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

}
测试结果
/*
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 70, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@42b28ff1]
小李查询到的价格为:70
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3c74aa0d] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1820594585 wrapping com.mysql.cj.jdbc.ConnectionImpl@76ad6715] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 70, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3c74aa0d]
小王查询到的价格为:70
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3743539f] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1927452108 wrapping com.mysql.cj.jdbc.ConnectionImpl@76ad6715] will not be managed by Spring
==>  Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 120(Integer), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3743539f]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ae62c7e] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@780570776 wrapping com.mysql.cj.jdbc.ConnectionImpl@76ad6715] will not be managed by Spring
==>  Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 40(Integer), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ae62c7e]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5d497a91] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@102185114 wrapping com.mysql.cj.jdbc.ConnectionImpl@76ad6715] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 120, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5d497a91]
老板查询到的价格为:120
*/
优化修改流程
    @Test
    public void testProduct01(){
        //小李查询价格
        Product productL = productMapper.selectById(1);
        System.out.println("小李查询到的价格为:"+productL.getPrice());
        //小王查询价格
        Product productW = productMapper.selectById(1);
        System.out.println("小王查询到的价格为:"+productW.getPrice());
        //小李修改价格
        productL.setPrice(productL.getPrice()+50);
        productMapper.updateById(productL);
        //小王修改价格
        productW.setPrice(productW.getPrice()-30);
        int result = productMapper.updateById(productW);
        if (result == 0){
            //修改失败
            Product productNew = productMapper.selectById(1);
            productNew.setPrice(productNew.getPrice()-30);
            productMapper.updateById(productNew);
        }
        //老板查询价格
        Product productB = productMapper.selectById(1);
        System.out.println("老板查询到的价格为:"+productB.getPrice());
    }

/*
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 100, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@282ffbf5]
小李查询到的价格为:100
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@378f002a] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@452817647 wrapping com.mysql.cj.jdbc.ConnectionImpl@f1d0004] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 100, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@378f002a]
小王查询到的价格为:100
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75181b50] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1869652507 wrapping com.mysql.cj.jdbc.ConnectionImpl@f1d0004] will not be managed by Spring
==>  Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75181b50]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4b0f2299] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@856047451 wrapping com.mysql.cj.jdbc.ConnectionImpl@f1d0004] will not be managed by Spring
==>  Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4b0f2299]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@352ed70d] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@117911771 wrapping com.mysql.cj.jdbc.ConnectionImpl@f1d0004] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 150, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@352ed70d]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@12704e15] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1361409513 wrapping com.mysql.cj.jdbc.ConnectionImpl@f1d0004] will not be managed by Spring
==>  Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 120(Integer), 2(Integer), 1(Long), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@12704e15]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2373ad99] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@862146308 wrapping com.mysql.cj.jdbc.ConnectionImpl@f1d0004] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 120, 2
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2373ad99]
老板查询到的价格为:120
*/

六、通用枚举

表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用MyBatisPlus的通用枚举来实现

添加sex字段
创建sex枚举类
package com.kj.mybatisplus.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;

@Getter
public enum SexEnum {
    MALE(1,"男"),
    FEMALE(2,"女");

    @EnumValue //将注解所标识的属性的值存储到数据库中
    private Integer sex;
    private String sexName;


    SexEnum(Integer sex, String sexName) {
        this.sex = sex;
        this.sexName = sexName;
    }
}
在实体类中添加sex属性
    private SexEnum sex;
测试
package com.kj.mybatisplus;

import com.kj.mybatisplus.enums.SexEnum;
import com.kj.mybatisplus.mapper.UserMapper;
import com.kj.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MybatisplusEnumTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        User user = new User();
        user.setName("kufufu");
        user.setAge(18);
        user.setSex(SexEnum.MALE);
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
    }

}

/*
==>  Preparing: INSERT INTO user ( name, age, sex ) VALUES ( ?, ?, ? )
==> Parameters: kufufu(String), 18(Integer), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@55259aa7]
result:1
*/

七、代码生成器

1、引入依赖

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>

2、快速生成

FastAutoGenerator.create("url", "username", "password")
    .globalConfig(builder -> {
        builder.author("baomidou") // 设置作者
            .enableSwagger() // 开启 swagger 模式
            .fileOverride() // 覆盖已生成文件
            .outputDir("D://"); // 指定输出目录
    })
    .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
        int typeCode = metaInfo.getJdbcType().TYPE_CODE;
        if (typeCode == Types.SMALLINT) {
            // 自定义类型转换
            return DbColumnType.INTEGER;
        }
        return typeRegistry.getColumnType(metaInfo);

    }))
    .packageConfig(builder -> {
        builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名
            .moduleName("system") // 设置父包模块名
            .pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径
    })
    .strategyConfig(builder -> {
        builder.addInclude("t_simple") // 设置需要生成的表名
            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
    })
    .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
    .execute();

八、多数据源

适用于多种场景:纯粹多库、读写分离、一主多从、混合模式等

目前我们就来模拟一个纯粹多库的一个场景,其他场景类似

场景说明:

我们创建两个库,分别为mybatis_plus(以前的库不动),与mybatis_plus_1(新增),将mybatis_plus库中的product表移动到mybatis_plus_1库,这样每个库一张表,通过一个测试用例分别获取用户数据与商品数据,如果获取到说明多库模拟成功

1、创建数据库以及表

创建数据库mybatis_plus_1和表product

CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4*/;
use `mybatis_plus_1`;
CREATE TABLE product(
 `id` BIGINT(20) NOT NULL COMMENT '主键ID',
 `name` VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
 `price` INT(11) DEFAULT 0 COMMENT '价格',
 `version` INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
 PRIMARY KEY (id)
);

添加测试数据

INSERT INTO product(id,name,price) VALUES(1,'外星人笔记本',100);

删除mybatis_plus中的product表

use mybatis_plus;
DROP TABLE IF EXISTS product;

2、引入依赖

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.6.1</version>
        </dependency>

3、配置多数据源

说明:注释掉之前的数据库连接,添加新配置

spring:
  #配置数据源信息
  datasource:
    dynamic:
      #设置默认的数据源或者数据源组,默认即为master
      primary: master
      #严格匹配数据源,默认false,true未匹配到指定数据源时抛出异常,false使用默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password:
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值