41-MybatisPlus

41-MybatisPlus-qianfeng-笔记


文章目录

  • 41-MybatisPlus-qianfeng-笔记
  • 一、了解Mybatis-Plus
    • ①:简介
    • ②:愿景
    • ③:特性
    • ④:支持数据库
    • ⑤:框架结构
    • ⑥: 代码托管
    • ⑦:作者
  • 二、入门案例
    • ①:创建SpringBoot工程
    • ②: 导入依赖
    • ③:数据准备
    • ④:代码实现
      • 01. 配置application.yml
      • 02. 实体类
      • 03. 创建mapper接口并扫描
    • ⑤:测试
    • ⑥:添加日志
    • ⑦:xml去掉所有日志(补)
    • ⑧:去掉Spring及MybatisPlus控制台输出logo
  • 三、基本CRUD
    • ①:BaseMapper
    • ②:BaseMapper添加
    • ③:BaseMapper删除
      • 01.根据id删除单个数据
      • 02. 根据map集合中所设置的条件删除信息
      • 03. 通过多个Id实现批量删除
    • ④:BaseMapper修改
    • ⑤:BaseMapper查询
      • 01. 根据id查询信息
      • 02. 根据多个id批量查询
      • 03. 根据map集合中所设置的条件查询信息
      • 04. 查询所有信息
    • ⑥:自定义功能
    • ⑦:通用Service接口
      • 01. IService
      • 02. 创建Service接口和实现类
      • 03. 测试查询记录数
      • 04. 测试 批量添加
  • 四、常用注解
    • ①:设置实体类所对应的表名
      • 00. 数据表user修改为t_user
      • 01. 方法一:@Table Name()
      • 02. 方法二:使用全局配置
    • ②:@TableId
      • 00. id生成策略(补)
      • 01. 给id列添加自增属性
      • 02.使用方法
      • 03. @TableId的Value属性
      • 04. @TableId的Type属性
      • 05. 配置全局主键策略:
    • ③:雪花算法
      • 01. 背景
      • 02. 数据库分表
      • 03. 垂直分表
      • 04.水平分表
    • ④:@TableField
    • ⑤:@TableLogic(逻辑删除)
  • 五、条件构造器和常用接口
    • ①:wapper介绍
    • ②:QueryWrapper
      • 01. 组装查询条件
      • 02. 组装排序条件
      • 03. 组装删除条件
      • 04. 修改功能
      • 05. 条件优先级
      • 06. 组装select字句(查询部分字段)
      • 07. 组装子查询
    • ③:UpdateWrapper
      • 01. 实现修改功能
    • ④:condition(先判断条件在组装)
    • ⑤:LambdaQueryWrapper
    • ⑥:LambdaUpdateWrapper
  • 六、插件
    • ①:分页插件
      • 01. 添加配置类
      • 02. 测试
      • 03. 分页相关数据获取
    • ②:xml自定义分页
      • 01. UserMapper中定义接口方法
      • 02. UserMapper.xml中编写SQL
      • 03. 配置类型别名所对应的包
      • 04.测试
    • ③:乐观锁
      • 01. 场景
      • 02. 模拟修改冲突
      • 03. 乐观锁实现流程
      • 04. 优化修改流程
  • 七、 通用枚举
    • ①:数据库表添加字段sex
    • ②:创建通用枚举类型
    • ③:字段sex添加到User实体类中
    • ④:配置扫描通用枚举
    • ⑤:测试
  • 八、代码生成器
    • ①:引入依赖
    • ②:快速生成
  • 九、多数据源
    • ①:创建数据库及表(数据准备)
    • ②:创建新的工程并完成配置
      • 01. 创建工程
      • 02. 导入依赖
      • 03. yml文件配置
      • 04.创建实体类
      • 05. 创建mapper接口
      • 06. 在启动类上添加扫描mapper的注解
      • 07. 创建service接口及实体类
    • ③:测试多数据源
  • 十、MyBatisX插件
    • ①:安装MyBatisX插件
    • ②:MyBatisX代码快速生成
      • 01. 创建新的工程
      • 02. 引入依赖
      • 03. 配置yml信息
      • 04. IDEA连接数据库
      • 05. 代码生成
    • ③:MyBatisX快速生成CRUD
      • 01. 自动生成添加方法
      • 02. 自动生成删除方法
      • 03. 自动生成修改方法
      • 04. 自动生成查询方法


一、了解Mybatis-Plus

①:简介

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

官网:https:/mybatis.plus/或https://baomidou.com/

②:愿景

愿景
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。

41-MybatisPlus_第1张图片

③:特性

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

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

④:支持数据库

任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。

  • MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb

  • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库

⑤:框架结构

41-MybatisPlus_第2张图片

⑥: 代码托管

  • Gitee :https://gitee.com/baomidou/mybatis-plus
  • Github:https://github.com/baomidou/mybatis-plus

⑦:作者

Mybatis-Plus是由baomidou(苞米豆)组织开发并且开源的,目前该组织大概有人左右。

  • 码云地址:https:/gitee.com/organizations/baomidou

二、入门案例

①:创建SpringBoot工程

1. 使用 Spring Initializr 快速初始化一个 Spring Boot 工程
41-MybatisPlus_第3张图片41-MybatisPlus_第4张图片
2. 删除无用文件
41-MybatisPlus_第5张图片

②: 导入依赖


        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>


        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.5.1version>
        dependency>

③:数据准备

# 创建数据库
create database mybatis_plus;

# 切换数据库
use mybatis_plus;

# 创建数据表
create table user(
  id bigint(20) not null comment '主键Id',
  name varchar(30) default null comment '姓名',
  age int default null comment '年龄',
  email varchar(50) default null comment '邮箱',
  primary key (id)
)engine = InnoDB default charset = utf8;

# 添加数据
insert into user(id, name, age, email) VALUES
(1,'Jone',18,'[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');

④:代码实现

01. 配置application.yml

spring:
  # 配置数据源信息
  datasource:
    # 配置数据源类型
    type: com.zaxxer.hikari.HikariDataSource
    # 配置连接数据库信息
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root
  • 注意:

1、驱动类driver-class-name

  • spring boot 2.0(内置jdbc5驱动),驱动类使用:
    driver-class-name: com.mysql.jdbc.Driver
  • spring boot 2.1及以上(内置jdbc8驱动),驱动类使用:
    driver-class-name: com.mysql.cj.jdbc.Driver
  • 否则运行测试用例的时候会有 WARN 信息

2、连接地址url

  • MySQL5.7版本的url:
    jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
  • MySQL8.0版本的url:
    jdbc:mysql://localhost:3306/mybatis_plus?
    serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
  • 否则运行测试用例报告如下错误:
    java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or
    represents more

02. 实体类

1. 添加实体
41-MybatisPlus_第6张图片
@Data
@AllArgsConstructor
public class User {

  private long id;
  private String name;
  private long age;
  private String email;
}

03. 创建mapper接口并扫描

BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的实体类型

1. 创建mapper接口并继承BaseMapper
41-MybatisPlus_第7张图片
2. 在Spring Boot启动类中添加@MapperScan注解,扫描mapper包
41-MybatisPlus_第8张图片
@SpringBootApplication
@MapperScan("com.it.mapper")
public class MybatisPlus01DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisPlus01DemoApplication.class, args);
    }

}

⑤:测试

1. 创建测试类
在这里插入图片描述
@SpringBootTest
class UserMapperTest {
    @Autowired
    private  UserMapper userMapper;

    @Test
    void testSelectList(){
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out :: println);
    }
}
41-MybatisPlus_第9张图片

⑥:添加日志

1. 在application.yml中配置日志输出
# 配置MyBatis日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
41-MybatisPlus_第10张图片

⑦:xml去掉所有日志(补)


<configuration>

configuration>

41-MybatisPlus_第11张图片

⑧:去掉Spring及MybatisPlus控制台输出logo

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?charcaterEnconding=utf-8
    username: root
    password: root
    # 去掉 Spring banner
  main:
    banner-mode: off

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 去掉 Mybatis-Plus banner
  global-config:
    banner: false



41-MybatisPlus_第12张图片

三、基本CRUD

①:BaseMapper

MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现,我们可以直接使用,接口如下:

public interface BaseMapper<T> extends Mapper<T> {

    /**
     * 插入一条记录
     *
     * @param entity 实体对象
     */
    int insert(T entity);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    int deleteById(Serializable id);

    /**
     * 根据实体(ID)删除
     *
     * @param entity 实体对象
     * @since 3.4.4
     */
    int deleteById(T entity);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 删除(根据ID或实体 批量删除)
     *
     * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)
     */
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> idList);

    /**
     * 根据 ID 修改
     *
     * @param entity 实体对象
     */
    int updateById(@Param(Constants.ENTITY) T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象 (set 条件值,可以为 null)
     * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T selectById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,查询一条记录
     * 

查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常

* * @param queryWrapper 实体对象封装操作类(可以为 null) */
default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) { List<T> ts = this.selectList(queryWrapper); if (CollectionUtils.isNotEmpty(ts)) { if (ts.size() != 1) { throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records"); } return ts.get(0); } return null; } /** * 根据 Wrapper 条件,判断是否存在记录 * * @param queryWrapper 实体对象封装操作类 * @return */ default boolean exists(Wrapper<T> queryWrapper) { Long count = this.selectCount(queryWrapper); return null != count && count > 0; } /** * 根据 Wrapper 条件,查询总记录数 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 entity 条件,查询全部记录 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录 *

注意: 只返回第一个字段的值

* * @param queryWrapper 实体对象封装操作类(可以为 null) */
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 entity 条件,查询全部记录(并翻页) * * @param page 分页查询条件(可以为 RowBounds.DEFAULT) * @param queryWrapper 实体对象封装操作类(可以为 null) */ <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录(并翻页) * * @param page 分页查询条件 * @param queryWrapper 实体对象封装操作类 */ <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); }

②:BaseMapper添加

1. 插入
41-MybatisPlus_第13张图片
    @Test
    void testInsert(){
        // 实现新增用户信息
        User user = new User(7, "Coke", 23, "[email protected]");
        int i = userMapper.insert(user);
        System.out.println("i = " + i);
        System.out.println("Id = " + user.getId());
    }

③:BaseMapper删除

01.根据id删除单个数据

1. DELETE FROM user WHERE id=?
41-MybatisPlus_第14张图片
    @Test
    void testDelete(){
        // 通过id删除信息(DELETE FROM user WHERE id=?)
        int i = userMapper.deleteById(7);
        System.out.println("i = " + i);
    }

02. 根据map集合中所设置的条件删除信息

1.
41-MybatisPlus_第15张图片
    @Test
    void testDeleteByMap(){
        // 根据map集合中所设置的条件删除信息(DELETE FROM user WHERE name = ? AND age = ?)
        Map<String,Object> map = new HashMap<>();
        map.put("name","Coke");
        map.put("age",23);
        int j = userMapper.deleteByMap(map);
        System.out.println("j = " + j);
    }

03. 通过多个Id实现批量删除

1. DELETE FROM user WHERE id IN ( ? , ? , ? )
41-MybatisPlus_第16张图片
    @Test
    void testDeleteBatchIds(){
        // 通过多个Id实现批量删除(DELETE FROM user WHERE id IN ( ? , ? , ? ))
        List<Integer> list = Arrays.asList(1, 2, 3);
        int ids = userMapper.deleteBatchIds(list);
        System.out.println("ids = " + ids);
    }

④:BaseMapper修改

1. UPDATE user SET name=?, age=?, email=? WHERE id=?
41-MybatisPlus_第17张图片
    @Test
    void testUpdate(){
        // 修改用户信息(UPDATE user SET name=?, age=?, email=? WHERE id=?)
        User user = new User(0, "Coke", 23, "[email protected]");
        int i = userMapper.updateById(user);
        System.out.println("i = " + i);
    }

⑤:BaseMapper查询

01. 根据id查询信息

1. SELECT id,name,age,email FROM user WHERE id=?
41-MybatisPlus_第18张图片
    @Test
    void testSelectById(){
        // 通过Id查询信息(SELECT id,name,age,email FROM user WHERE id=?)
        User user = userMapper.selectById(0);
        System.out.println("user = " + user);
    }

02. 根据多个id批量查询

1. SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? , ? )
41-MybatisPlus_第19张图片
    @Test
    void testSelectBatchIds(){
        // 通过多个Id批量查询信息(SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? , ? ))
        List<Integer> list = Arrays.asList(1, 2, 3, 4);
        List<User> userList = userMapper.selectBatchIds(list);
        userList.forEach(System.out ::println);
    }

03. 根据map集合中所设置的条件查询信息

1. SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
41-MybatisPlus_第20张图片
    @Test
    void testSelectByMap(){
        // 根据map集合中所设置的条件查询信息(SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? , ? ))
        Map<String,Object> map = new HashMap<>();
        map.put("name","Coke");
        map.put("age",23);
        List<User> userList = userMapper.selectByMap(map);
        userList.forEach(System.out ::println);
    }

04. 查询所有信息

1. SELECT id,name,age,email FROM user
41-MybatisPlus_第21张图片
    @Test
    void testSelectList(){
        // 查询所有的信息( SELECT id,name,age,email FROM user)
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out ::println);
    }

⑥:自定义功能

自定义查询方法(根据id查询)返回值为Map集合

1. 创建mapper目录 在mapper文件中创建xml配置文件
41-MybatisPlus_第22张图片
<?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">
<mapper namespace="com.it.mapper.UserMapper">

</mapper>
2. 在Mapper接口中定义查询方法
41-MybatisPlus_第23张图片
Map<String, Object> selectMapById(Integer id);
3. 在xml中编写SQL语句
41-MybatisPlus_第24张图片
    <select id="selectMapById" resultType="map">
        select id, name, age, email
        from user
        where id = #{id};
    select>
4. 测试
41-MybatisPlus_第25张图片
    @Test
    void testSelectMapById(){

        Map<String, Object> map = userMapper.selectMapById(0);
        System.out.println("map = " + map);
    }

⑦:通用Service接口

说明:

  • 通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删
    除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承
    Mybatis-Plus 提供的基类
  • 官网地址:https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%
    A3

01. IService

MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑
详情查看源码IService和ServiceImpl

02. 创建Service接口和实现类

1. 创建Service接口
41-MybatisPlus_第26张图片
/**
 * UserService继承IService模板提供的基础功能
 */
public interface UserService extends IService<User> {

}
2. 创建实现类
41-MybatisPlus_第27张图片
/**
 * ServiceImpl实现了IService,提供了IService中基础功能的实现
 * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
 */
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}

03. 测试查询记录数

1. 在Service实现类上添加@Service注解
41-MybatisPlus_第28张图片
2. 创建测试类
41-MybatisPlus_第29张图片
@SpringBootTest
class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    void getCount(){
        // 查询记录数(SELECT COUNT( * ) FROM user)
        long count = userService.count();
        System.out.println("count = " + count);
    }
}

04. 测试 批量添加

1. 测试
41-MybatisPlus_第30张图片 41-MybatisPlus_第31张图片
    @Test
    void testInsertMore(){
        // 批量添加(INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ))
        ArrayList<User> users = new ArrayList<>();
        for (int i = 1; i <= 6; i++) {
            User user = new User(7 + i,"Coke" + i,20 + i,"Coke@it" + i + ".com");
            users.add(user);
        }
        boolean b = userService.saveBatch(users);
        System.out.println(b);
    }

四、常用注解

①:设置实体类所对应的表名

00. 数据表user修改为t_user

alter table user rename t_user;

01. 方法一:@Table Name()

1. 修改表名之后直接测试(会报错)
41-MybatisPlus_第32张图片
2. 解决方法一: 在实体类上加注解@TableName()
41-MybatisPlus_第33张图片
3. 测试
41-MybatisPlus_第34张图片

02. 方法二:使用全局配置

1. 在配置文件中配置全局配置
41-MybatisPlus_第35张图片
mybatis-plus:
# 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      table-prefix: t_
2. 测试
41-MybatisPlus_第36张图片

②:@TableId

00. id生成策略(补)

41-MybatisPlus_第37张图片

01. 给id列添加自增属性

# 给id列添加主键
alter table t_user add primary key (id);

# 给id列添加自增属性
alter table t_user modify id bigint auto_increment;

# 给id列同时添加主键和自增属性
alter table t_user change id id bigint auto_increment primary key;

当我用SQLyog尝试修改已有记录的mysql数据表的主键为自动增长时,报出以下错误

ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry ’1′ for key ‘PRIMARY’

解决方法:

  • 第1步:将主键字段值为0的那条记录值改为其他大于0且不重复的任意数

  • 第2步:修改主键字段为auto_increment

  • 第3步:把刚才修改过的那条记录的值还原

02.使用方法

主键专属

比如数据中的表中的字段是id

但是实体类是uId

那么就需要在uId上打上这个注解@TableId

41-MybatisPlus_第38张图片

03. @TableId的Value属性

41-MybatisPlus_第39张图片

04. @TableId的Type属性

type属性用来定义主键策略

常用的主键策略:

描述
IdType.ASSIGN_ID(默认) 基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
IdType.AUTO 使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,否则无效
41-MybatisPlus_第40张图片

05. 配置全局主键策略:

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

# 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      table-prefix: t_
      # 配置MyBatis-Plus的主键策略
      id-type: auto

③:雪花算法

01. 背景

需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。
数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。

02. 数据库分表

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

41-MybatisPlus_第41张图片

03. 垂直分表

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

例如,前面示意图中的 nickname 和 description 字段,假设我们是一个婚恋网站,用户在筛选其他用户的时候,主要是用 age 和 sex 两个字段进行查询,而 nickname 和 description 两个字段主要用于展示,一般不会在业务查询中用到。description 本身又比较长,因此我们可以

将这两个字段独立到另外一张表中,这样在查询 age 和 sex 时,就能带来一定的性能提升。

04.水平分表

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

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

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

主键自增

①以最常见的用户 ID 为例,可以按照 1000000 的范围大小进行分段,1 ~ 999999 放到表 1中,1000000 ~ 1999999 放到表2中,以此类推。

②复杂点:分段大小的选取。分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会导致单表依然存在性能问题,一般建议分段大小在 100 万至 2000 万之间,具体需要根据业务选取合适的分段大小。

③优点:可以随着数据的增加平滑地扩充新的表。例如,现在的用户是 100 万,如果增加到 1000 万,只需要增加新的表就可以了,原有的数据不需要动。

④缺点:分布不均匀。假如按照 1000 万来进行分表,有可能某个分段实际存储的数据量只有 1 条,而另外一个分段实际存储的数据量有 1000 万条。

取模

①同样以用户 ID 为例,假如我们一开始就规划了 10 个数据库表,可以简单地用 user_id % 10 的值来表示数据所属的数据库表编号,ID 为 985 的用户放到编号为 5 的子表中,ID 为 10086 的用户放到编号为 6 的子表中。

②复杂点:初始表数量的确定。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。

③优点:表分布比较均匀。

④缺点:扩充新的表很麻烦,所有数据都要重分布。

雪花算法

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

①核心思想:

长度共64bit(一个long型)。

首先是一个符号位,1bit标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0。

41bit时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年。

10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID,可以部署在1024个节点)。

12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。

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

④:@TableField

经过以上的测试,我们可以发现,MyBatis-Plus在执行SQL语句时,要保证实体类中的属性名和
表中的字段名一致
如果实体类中的属性名和字段名不一致的情况,会出现什么问题呢?

  • 情况一:

若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格
例如实体类属性userName,表中字段user_name
此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格
相当于在MyBatis中配置

  • 情况二:

若实体类中的属性和表中的字段不满足情况1
例如实体类属性name,表中字段user_name
此时需要在实体类属性上使用@TableField(“user_name”)设置属性所对应的字段名

41-MybatisPlus_第43张图片

⑤:@TableLogic(逻辑删除)

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
  • 使用场景:可以进行数据恢复
1. 在数据表中添加一个字段默认值为0
41-MybatisPlus_第44张图片
2. 在实体类中添加对应属性(并标记为逻辑删除)
41-MybatisPlus_第45张图片
3. 测试 删除一些信息
41-MybatisPlus_第46张图片41-MybatisPlus_第47张图片
4. 测试(查询信息)
41-MybatisPlus_第48张图片

41-MybatisPlus_第49张图片

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

①:wapper介绍

41-MybatisPlus_第50张图片

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

②:QueryWrapper

01. 组装查询条件

参考文档:https://baomidou.com/pages/10c804/#eq

    @Test
    void testQueryWrapper(){
        // 查询用户名包含a,年龄在20到23之间,邮箱信息不为nuLL的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("user_name","a"). // 模糊查询 "user_name"列包含"a"
                between("age",20,23). // 查询 年龄在 20-23之间
                isNotNull("email");               // 查询 email 不为空的
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out :: println);
    }
1. 创建测试方法
41-MybatisPlus_第51张图片

02. 组装排序条件

    @Test
    void testOrderByWrapper() {
        // 按年龄降序查询用户,如果年龄相同则按id升序排列
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("age")
                .orderByAsc("uid");
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out :: println);
    }
1. 添加测试方法
41-MybatisPlus_第52张图片41-MybatisPlus_第53张图片

03. 组装删除条件

    @Test
    void testDeleteByWrapper() {
        // 删除邮箱地址为nuLL的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNull("email");
        int i = userMapper.delete(queryWrapper);
        System.out.println(i);
    }
1. 添加测试方法
41-MybatisPlus_第54张图片

04. 修改功能

    @Test
    void testUpdateByMrapper(){
        // 修改(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age",20)
                .like("user_name","a")
                .or()
                .isNull("email");
        User user = new User(21, "雪碧", 22, "[email protected]", 0);
        int update = userMapper.update(user, queryWrapper);
        System.out.println(update);
    }
1. 添加测试
41-MybatisPlus_第55张图片

05. 条件优先级

    @Test
    void testUpdateByMrapper2(){
        // 修改用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息
        //lambda表达式内的逻辑优先运算
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .like("user_name","a")
                .and(i -> i.gt("age",20)
                        .or().isNull("email"));
        User user = new User(21, "雪碧", 22, "[email protected]", 0);
        int update = userMapper.update(user, queryWrapper);
        System.out.println(update);
    }
1. 测试
41-MybatisPlus_第56张图片

06. 组装select字句(查询部分字段)

方法一: 不使用Lambda表达式

    @Test
    void testSelectByWrapper(){
        // 查询用户信息的username和age字段
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("user_name","age","email");
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        maps.forEach(System.out ::println);
    }
1. 测试
41-MybatisPlus_第57张图片 41-MybatisPlus_第58张图片

方二一: 使用Lambda表达式(补)

    @Autowired(required = false)
    private UserMapper userMapper;

    @Test
    void  test4() {
        // 只查部分字段
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(User::getId,User::getAge,User::getUserName);
        // 设置分页 从第五页开始,每页显示3条数据
        Page<User> userPage = new Page<>(7,3);
        Page<User> page = userMapper.selectPage(userPage, queryWrapper);
        List<User> users = page.getRecords();

        users.forEach(System.out ::println);
    }
41-MybatisPlus_第59张图片
41-MybatisPlus_第60张图片

07. 组装子查询

    @Test
    void testSelectByWrapper2(){
        // 查询id小于等于10的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.inSql("uid","select uid from t_user where uid <= 10");
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out ::println);
    }
1. 测试
41-MybatisPlus_第61张图片

③:UpdateWrapper

01. 实现修改功能

    @Test
    void testUpdateWrapper(){
        // 修改(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.like("user_name","a")
                // lambda表达式内的逻辑优先运算
                .and(i -> i.gt("age",20).or().isNull("email"));
        // 组装set子句以及修改条件
        updateWrapper.set("user_name","小白");
        // 这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
        int update = userMapper.update(null, updateWrapper);
        System.out.println(update);
    }
1. 测试
41-MybatisPlus_第62张图片

④:condition(先判断条件在组装)

在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则一定不能组装,以免影响SQL执行的结果

    @Test
    void testCondition(){
        String username = "a";
        Integer ageBegin = null;
        Integer ageEnd = 30;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(username),"user_name","a")
                .ge(ageBegin != null, "age",ageBegin)
                .le(ageEnd != null, "age", ageEnd);

        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out ::println);

    }
1. 测试
41-MybatisPlus_第63张图片

⑤:LambdaQueryWrapper

    @Test
    void testLambdaQueryWrapper(){
        String username = "a";
        Integer ageBegin = null;
        Integer ageEnd = 30;
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(username),User::getName,username)
                .ge(ageBegin != null,User::getAge,ageBegin)
                .le(ageEnd != null,User::getAge,ageEnd);
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out ::println);
    }
1. 测试
41-MybatisPlus_第64张图片

⑥:LambdaUpdateWrapper

    @Test
    void testLambdaUpdateWrapper() {
        // 修改(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.like(User::getName, "a")
                .and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));
        updateWrapper.set(User::getName, "小白");
        int i = userMapper.update(null, updateWrapper);
        System.out.println(i);
    }
1. 测试
41-MybatisPlus_第65张图片

六、插件

①:分页插件

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

01. 添加配置类

@Configuration
@MapperScan("com.it.mapper") //可以将启动类中的注解移到此处
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
1. 创建config目录并在目录中创建MyBatisPlusConfig类
41-MybatisPlus_第66张图片

02. 测试

1. 测试(查询第一页)
41-MybatisPlus_第67张图片
2. 查询第二页
41-MybatisPlus_第68张图片

03. 分页相关数据获取

    @Test
    void testPage(){
        Page<User> userPage = new Page<>(3,3);
        Page<User> page = userMapper.selectPage(userPage, null);
        System.out.println("page.getRecords() = " + page.getRecords()); // 当前页的数据
        System.out.println("page.getPages() = " + page.getPages()); // 总页数
        System.out.println("page.getTotal() = " + page.getTotal()); // 总记录数
        System.out.println("page.hasNext() = " + page.hasNext()); // 是否有下一页
        System.out.println("page.hasPrevious() = " + page.hasPrevious()); // 是否有上一页
    }
1. 测试
41-MybatisPlus_第69张图片

②:xml自定义分页

01. UserMapper中定义接口方法

    /**
     * 根据年龄查询用户列表,分页显示
     * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位
     * @param age 年龄
     */
    Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
41-MybatisPlus_第70张图片

02. UserMapper.xml中编写SQL

    <select id="selectPageVo" resultType="User">
        select uid, user_name, age, email
        from t_user
        where age > #{age}
    </select>
41-MybatisPlus_第71张图片

03. 配置类型别名所对应的包

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

# 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      table-prefix: t_
      # 配置MyBatis-Plus的主键策略
      id-type: auto
      # 配置类型别名所对应的包
  type-aliases-package: com.it.pojo
41-MybatisPlus_第72张图片

04.测试

    @Test
    void testPage(){
        // 设置分页参数
        Page<User> userPage = new Page<>(1,5);
        Page<User> page = userMapper.selectPageVo(userPage, 21);
        // 获取分页数据
        System.out.println("page.getRecords() = " + page.getRecords()); // 当前页的数据
        System.out.println("page.getPages() = " + page.getPages()); // 总页数
        System.out.println("page.getTotal() = " + page.getTotal()); // 总记录数
        System.out.println("page.hasNext() = " + page.hasNext()); // 是否有下一页
        System.out.println("page.hasPrevious() = " + page.hasPrevious()); // 是否有上一页
    }
41-MybatisPlus_第73张图片

③:乐观锁

01. 场景

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

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

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

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

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

02. 模拟修改冲突

1. 数据库中增加商品表
# 增加商品表
create table t_product
(
    id      bigint(20) primary key not null comment '主键',
    name    varchar(30)            not null comment '商品名称',
    price   int(11) default 0 comment '价格',
    version int(11) default 0 comment '乐观锁版本号'
);
2. 添加数据
insert into t_product(id, name, price)
VALUES (1, '外星人笔记本', 100);
3. 添加实体类
@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;
    private Integer version;
}
4. 添加mapper
41-MybatisPlus_第74张图片
// 继承BaseMapper才能使用MybatisPlus使用其中的方法
public interface ProductMapper extends BaseMapper<Product> {
}
5. 测试
41-MybatisPlus_第75张图片
@SpringBootTest
class ProductMapperTest {

    @Autowired(required = false)
    private ProductMapper productMapper;

    @Test
    void testConcurrentUpdate(){
        // 1. 小李
        Product p1 = productMapper.selectById(1);
        System.out.println("小李取出的价格:" + p1.getPrice());

        // 2. 小王
        Product p2 = productMapper.selectById(1);
        System.out.println("小王取出的价格:" + p2.getPrice());

        // 3. 小李将价格加了50元,存入了数据库
        p1.setPrice(p1.getPrice()  + 50);
        int result1 = productMapper.updateById(p1);
        System.out.println("小李修改结果:" + result1);

        // 4、小王将商品减了30元,存入了数据库
        p2.setPrice(p2.getPrice() - 30);
        int result2 = productMapper.updateById(p2);
        System.out.println("小王修改结果:" + result2);

        // 最后的结果
        Product p3 = productMapper.selectById(1L);
        // 价格覆盖,最后的结果:70
        System.out.println("最后的结果:" + p3.getPrice());
    }
}

03. 乐观锁实现流程

1. 数据库中添加version字段
41-MybatisPlus_第76张图片
2. 添加乐观锁插件配置
41-MybatisPlus_第77张图片
@Configuration
@MapperScan("com.it.mapper") //可以将启动类中的注解移到此处
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        // 添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}
3. 修改数据库信息
41-MybatisPlus_第78张图片
4. 测试
41-MybatisPlus_第79张图片
@SpringBootTest
class ProductMapperTest {

    @Autowired(required = false)
    private ProductMapper productMapper;

    @Test
    void testConcurrentUpdate(){
        // 1. 小李
        Product p1 = productMapper.selectById(1);
        System.out.println("小李取出的价格:" + p1.getPrice());

        // 2. 小王
        Product p2 = productMapper.selectById(1);
        System.out.println("小王取出的价格:" + p2.getPrice());

        // 3. 小李将价格加了50元,存入了数据库
        p1.setPrice(p1.getPrice()  + 50);
        int result1 = productMapper.updateById(p1);
        System.out.println("小李修改结果:" + result1);

        // 4、小王将商品减了30元,存入了数据库
        p2.setPrice(p2.getPrice() - 30);
        int result2 = productMapper.updateById(p2);
        System.out.println("小王修改结果:" + result2);

        // 最后的结果
        Product p3 = productMapper.selectById(1L);
        // 小王的修改被驳回,最后的结果:150
        System.out.println("最后的结果:" + p3.getPrice());
    }
}

04. 优化修改流程

1. 修改数据库信息
41-MybatisPlus_第80张图片
2. 测试
41-MybatisPlus_第81张图片
@SpringBootTest
class ProductMapperTest {

    @Autowired(required = false)
    private ProductMapper productMapper;

    @Test
    void testConcurrentUpdate(){
        // 1. 小李
        Product p1 = productMapper.selectById(1);
        System.out.println("小李取出的价格:" + p1.getPrice());

        // 2. 小王
        Product p2 = productMapper.selectById(1);
        System.out.println("小王取出的价格:" + p2.getPrice());

        // 3. 小李将价格加了50元,存入了数据库
        p1.setPrice(p1.getPrice()  + 50);
        int result1 = productMapper.updateById(p1);
        System.out.println("小李修改结果:" + result1);

        // 4、小王将商品减了30元,存入了数据库
        p2.setPrice(p2.getPrice() - 30);
        int result2 = productMapper.updateById(p2);
        // 判断result2 是否等于0 等于0修改失败
        if(result2 == 0){
            // 修改失败 重试 先查询(更新版本号)
            Product productNew = productMapper.selectById(1);
            productNew.setPrice(productNew.getPrice() - 30);
            result2 = productMapper.updateById(productNew);
        }
        System.out.println("小王修改结果:" + result2);

        // 最后的结果
        Product p3 = productMapper.selectById(1L);
        // 小王的修改被驳回,最后的结果:120
        System.out.println("最后的结果:" + p3.getPrice());
    }
}

七、 通用枚举

①:数据库表添加字段sex

1. 数据库表添加字段sex
41-MybatisPlus_第82张图片

②:创建通用枚举类型

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

    /**
     * 在需要存储数据库的属性上添加@EnumValue注解,
     * 在需要前端展示的属性上添加@JsonValue注解;
     */
    @EnumValue
    private Integer sex;
    private String sexName;

    EnumSex(Integer sex, String sexName) {
        this.sex = sex;
        this.sexName = sexName;
    }
}
1. 创建通用枚举类型
41-MybatisPlus_第83张图片

③:字段sex添加到User实体类中

1.
41-MybatisPlus_第84张图片

④:配置扫描通用枚举

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

  # 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      table-prefix: t_
      # 配置MyBatis-Plus的主键策略
      id-type: auto
      # 配置类型别名所对应的包
  type-aliases-package: com.it.pojo

  # 配置扫描通用枚举
  type-enums-package: com.it.enums

⑤:测试

    @Test
    void testEnum() {
        //设置性别信息为枚举项,会将@EnumValue注解所标识的属性值存储到数据库
        User user = new User(21, "雪碧", 22, "[email protected]",EnumSex.MALE, 0);
        int i = userMapper.insert(user);
        System.out.println(i);
    }
41-MybatisPlus_第85张图片

八、代码生成器

①:引入依赖


        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-generatorartifactId>
            <version>3.5.1version>
        dependency>

        <dependency>
            <groupId>org.freemarkergroupId>
            <artifactId>freemarkerartifactId>
            <version>2.3.31version>
        dependency>

②:快速生成

public class FastAutoGeneratorTest {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8", "root", "root")
                        .globalConfig(builder -> {
                            builder.author("coke") // 设置作者
                                 // .enableSwagger() // 开启 swagger 模式
                                    .fileOverride() // 覆盖已生成文件
                                    .outputDir("D://OOP//java//develop_idea//05_myBatisPlus"); // 指定输出目录
                        })
                        .packageConfig(builder -> {
                            builder.parent("com.it") // 设置父包名
                                    .moduleName("mybatisplus") // 设置父包模块名
                                    .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://OOP//java//develop_idea//05_myBatisPlus")); // 设置mapperXml生成路径
                        })
                        .strategyConfig(builder -> {
                            builder.addInclude("t_user") // 设置需要生成的表名
                                    .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                        })
                        .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                        .execute();
        }
    }
41-MybatisPlus_第86张图片41-MybatisPlus_第87张图片

九、多数据源

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

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

场景说明:

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

①:创建数据库及表(数据准备)

1. 创建数据库mybatis_plus_1和表product
CREATE DATABASE mybatis_plus_1;

USE mybatis_plus_1;

CREATE TABLE product(
	id BIGINT(20) not NULL PRIMARY KEY  COMMENT '主键',
	name VARCHAR(30) not NULL COMMENT '商品名称',
	price INT(11) DEFAULT 0  COMMENT '价格',
	VERSION INT(11) DEFAULT 0  COMMENT '乐观锁版本号'
);
2. 添加测试数据
INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
3. 删除mybatis_plus库product表
use mybatis_plus;
DROP TABLE IF EXISTS t_product;

②:创建新的工程并完成配置

01. 创建工程

1. 创建工程
41-MybatisPlus_第88张图片
2. 选择SpringBoot版本
41-MybatisPlus_第89张图片
3. 删除不需要的配置
41-MybatisPlus_第90张图片

02. 导入依赖

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>

        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>

        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.5.1version>
        dependency>


        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>dynamic-datasource-spring-boot-starterartifactId>
            <version>3.5.0version>
        dependency>

03. yml文件配置

spring:
  # 配置数据源信息
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: root
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: root

04.创建实体类

1. 创建User实体类
41-MybatisPlus_第91张图片
@Data
@AllArgsConstructor
@TableName("t_user")
public class User {

    @TableId
    private Integer uid;
    private String userName;
    private Integer age;
    private Integer sex;
    private String email;
    private Integer isDeleted;

}
2. 创建Product实体类
41-MybatisPlus_第92张图片
@Data
@AllArgsConstructor
public class Product {

    private Integer id;
    private String name;
    private Integer price;
    private Integer version;
}

05. 创建mapper接口

1. 创建UserMapper
41-MybatisPlus_第93张图片
public interface UserMapper extends BaseMapper<User> {
}
2. 创建ProductMapper
41-MybatisPlus_第94张图片
public interface ProductMapper extends BaseMapper<Product> {
}

06. 在启动类上添加扫描mapper的注解

41-MybatisPlus_第95张图片

07. 创建service接口及实体类

1. 创建UserService
41-MybatisPlus_第96张图片
public interface UserService extends IService<User> {
}
2. 创建UserService实现类UserServiceImpl
41-MybatisPlus_第97张图片
@Service
@DS("master")  // 使用的数据源
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
3. 创建Product接口
41-MybatisPlus_第98张图片
public interface ProductService extends IService<Product> {

}
4. 创建ProductService接口实现类ProductServiceImpl
41-MybatisPlus_第99张图片
@Service
@DS("slave_1")
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}

③:测试多数据源

@SpringBootTest
public class MyBatisPlusDatasourceTest {

    @Autowired
    private UserService userService;

    @Autowired
    private ProductService productService;

    @Test
    void testDatasource(){
        User user = userService.getById(1);
        Product product = productService.getById(1);
        System.out.println("user = " + user);
        System.out.println("product = " + product);
    }
}
1. 测试
41-MybatisPlus_第100张图片
结果:
1、都能顺利获取对象,则测试成功
2、如果我们实现读写分离,将写操作方法加上主库数据源,读操作方法加上从库数据源,自动切换,是不是就能实现读写分离?

十、MyBatisX插件

MyBatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率

但是在真正开发过程中,MyBatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表联查,我们就需要自己去编写代码和SQL语句,我们该如何快速的解决这个问题呢,这个时候可以使用MyBatisX插件

MyBatisX一款基于 IDEA 的快速开发插件,为效率而生。

  • MyBatisX插件用法:https://baomidou.com/pages/ba5b24/

①:安装MyBatisX插件

1. 安装
41-MybatisPlus_第101张图片
2. 创建UserMapper.xml文件
41-MybatisPlus_第102张图片

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.it.mapper.UserMapper">
mapper>

②:MyBatisX代码快速生成

01. 创建新的工程

1. 创建工程
41-MybatisPlus_第103张图片41-MybatisPlus_第104张图片
2. 删除不需要的文件
41-MybatisPlus_第105张图片

02. 引入依赖

  <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>

        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>

        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.5.1version>
        dependency>


        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>dynamic-datasource-spring-boot-starterartifactId>
            <version>3.5.0version>
        dependency>

03. 配置yml信息

spring:
  # 配置数据源信息
  datasource:
    # 配置数据源类型
    type: com.zaxxer.hikari.HikariDataSource
    # 配置连接数据库信息
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root

04. IDEA连接数据库

41-MybatisPlus_第106张图片

05. 代码生成

1. 点击MybatisX-Generator(生成器)
41-MybatisPlus_第107张图片
2.
41-MybatisPlus_第108张图片
3.
41-MybatisPlus_第109张图片
4.
41-MybatisPlus_第110张图片

③:MyBatisX快速生成CRUD

01. 自动生成添加方法

1. 生成添加方法
41-MybatisPlus_第111张图片

02. 自动生成删除方法

41-MybatisPlus_第112张图片

03. 自动生成修改方法

41-MybatisPlus_第113张图片

04. 自动生成查询方法

41-MybatisPlus_第114张图片

你可能感兴趣的:(Java,java-Spring,mybatis,java,mysql)