初学MyBatis-Plus

1 mybatis-plus概念

1.1 Mybatis-Plus简介

Mybatis-Plus是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。Mybatis-Plus提供了通用的mapper和service,可以在不编写任何SQL语句的情况下,快速的实现对单表的CRUD、批量、逻辑删除分页等操作。
在这里插入图片描述

1.2 mybatis-plus 特性
  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表delete、update操作智能分析阻断,也可自定义拦截规则,预防误操作
1.3 结构

在这里插入图片描述

2 mybatis-plus 基础使用

2.1 基本使用配置

在pom文件中引入一下依赖:

 <!-- mybatis-plus启动器 -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.1</version>
</dependency>

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

配置基本的application.yml

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource # 使用mybatis-plus需要配置该类型
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
    username: root
    password: root

# 日志,根据需要配置即可
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 配置数据库表名前缀,这样实体类名就可以跟表名不一致:如表t_user --> 实体类: User
  global-config:
    db-config:
      table-prefix: t_
      # 设置统一的主键生成策略
      id-type: auto

测试使用

2.1 创建一张user表
CREATE TABLE `user` (
	`id` BIGINT(20) NOT NULL COMMENT '主键ID',
	`name` VARCHAR(30) DEFAULT NULL COMMENT '姓名',
	`age` INT(11) DEFAULT NULL COMMENT '年龄',
	`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
	PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;
2.1.2 创建实体类User以及UserMapper接口
package com.yamei.mybatisplus.pojo;

import lombok.Data;

@Data
public class User {
  private Long id;

  private String name;

  private Integer age;

  private String email;
}

package com.yamei.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yamei.mybatisplus.pojo.User;
import org.springframework.stereotype.Repository;

// 使mapper永久有效,不写也没关系,只不过注入该mapper时候,编辑器会显示红色错误,不影响使用
@Repository
public interface UserMapper extends BaseMapper<User> {
}

/**
* BaseMapper 包含了单表操作的各种基础接口,mybatis-plus根据我们传入的
* 实体类去操作对应的数据表
*/
2.1.3 测试调用
package com.yamei.mybatisplus;

import com.yamei.mybatisplus.mapper.UserMapper;
import com.yamei.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
  private UserMapper userMapper;

  @Test
  public void testUser() {
    List<User> users = userMapper.selectList(null);

    System.out.println(users.toString());
  }
  
  @Test
  public void testDelById() {
    int i = userMapper.deleteById(1735561917386240002L);

    System.out.println("-------删除成功--------" + i);
  }
}

在这里插入图片描述

2.2 通用service

创建一个service接口文件

package com.yamei.mybatisplus.service;

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

// 继承mybatis-plus的通用service: IService
public interface UserService extends IService<User> {
    // 自定义接口
}

创建一个实现类文件

package com.yamei.mybatisplus.service.impl;

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

/**
 * UserServiceImpl 实现类不需要实现mybatis-plus的所有service接口,
 * 我们只需要继承mybatis-plus的基础实现类(ServiceImpl<对应的Mapper, 实体类>)即可
 */
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 自定义实现类
}

注意:自定义SQL和接口、service等按照之前的mybatis使用方式即可

2.3 通用注解
2.3.1 @TableName:用来指定表名
package com.yamei.mybatisplus.pojo;

import lombok.Data;

@Data
@TableName("t_user") // 指定该实体类使用的表名, 当实体类名与表名不一致的时候可用
public class User {
  private Long id;

  private String name;

  private Integer age;

  private String email;
}
2.3.2 @TableId:指定数据表的主键
package com.yamei.mybatisplus.pojo;

import lombok.Data;

@Data
public class User {
  // 将属性所对应的字段指定为主键
  // 该注解的value属性用于指定主键的字段@TableId(value="uid")==@TableId("uid")
  /** @TableId(value="uid", type=IdType.AUTO)
   * 该注解的type属性为主键生成类型,默认是IdType。ASSIGN_ID(基于雪花算法生成),
   * IdType.AUTO 则是使用数据库id自增策略(这个需要建表时候设置了id自增才可使用),
   * 其它类型可自行查看文档或源码
   */
  @TableId("uid")
  private Long id;

  private String name;

  private Integer age;

  private String email;
}
2.3.3 @TableLogic

a. 逻辑删除

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

b. 实现逻辑删除

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

package com.yamei.mybatisplus.pojo;

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

@Data
public class User {
  private Long id;

  private String name;

  private Integer age;

  private String email;
  
  @TableLogic // 逻辑删除
  private Integer isDeleted;
}

3 Wrapper 介绍

在这里插入图片描述

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

a.例1:组装查询条件

@Test
public void queryTest01() {
    // 查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息
    // sql:SELECT id,name,age,email FROM user WHERE (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL);
    
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("name", "a")
            .between("age", 20, 30)
            .isNotNull("email");
    
    List<User> users = userMapper.selectList(queryWrapper);
    
    users.forEach(System.out::println);
}

b.例2:组装排序条件

@Test
public void queryTest02() {
    // 查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
    // SELECT id,name,age,email FROM user ORDER BY age DESC,id ASC
    
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.orderByDesc("age").orderByAsc("id");

    List<User> users = userMapper.selectList(queryWrapper);

    users.forEach(System.out::println);
}

c.例3:组装删除条件

@Test
public void queryTest03() {
    // 删除邮箱地址为null的用户信息
    // UPDATE user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL); User实体类中使用了逻辑删除
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.isNull("email");

    int delete = userMapper.delete(queryWrapper);
    System.out.println("====delete====" + delete);
}

d.例4:使用QueryWrapper实现修改功能

@Test
public void queryTest04() {
    // 将 (年龄大于20并且用户名中包含有a) 或邮箱为null的用户信息修改
    // UPDATE user SET name=?, email=? WHERE is_deleted=0 AND (age > ? AND name LIKE ? OR email IS NULL)
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.gt("age", 20)
            .like("name", "a")
            .or()
            .isNull("email");

    User user = new User();
    user.setName("update");
    user.setEmail("test04@qq.com");

    int update = userMapper.update(user, queryWrapper);
    System.out.println("===update===" + update);
}

e.例5:条件的优先级

@Test
public void queryTest05() {
    // 将用户名中包含有a并且(年龄大于25或邮箱为null)的用户信息修改
    // UPDATE user SET name=?, email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("name", "a")
            .and(i -> i.gt("age", 25).or().isNull("email"));

    User user = new User();
    user.setName("小红");
    user.setEmail("xiaohong@qq.com");

    int update = userMapper.update(user, queryWrapper);

    System.out.println("===update===" + update);
}

f.例6:组装select子句

@Test
public void queryTest06() {
    // 查询用户的用户名、年龄、邮箱信息
    // SELECT name,age,email FROM user WHERE is_deleted=0
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.select("name", "age", "email");

    // List<User> users = userMapper.selectList(queryWrapper);
    List<Map<String, Object>> users = userMapper.selectMaps(queryWrapper);

    users.forEach(System.out::println);
}

g.例7:组装子查询

@Test
public void queryTest07() {
    // 使用子查询:查询id小于等于5的用户信息
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (id IN (select id from user where id <=5))
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.inSql("id", "select id from user where id <=5");

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

h.例8:使用condition组装条件

@Test
public void queryTest08() {
    String name = "a";
    Integer ageBegin = null;
    Integer ageEnd = 30;

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like(StringUtils.isNotBlank(name), "name", name)
            .ge(ageBegin != null, "age", ageBegin)
            .le(ageEnd != null, "age", ageEnd);

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

在这里插入图片描述

3.2 UpdateWrapper

a.例1:实现修改功能

@Test
public void updateTest01() {
    // 将用户名中包含有a并且(年龄大于25或邮箱为null)的用户信息修改
    // UPDATE user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.like("name", "a")
            .and(i -> i.gt("age", 25).or().isNull("email"));
    updateWrapper.set("name", "小黑").set("email", "xiaohei@qq.com");

    int update = userMapper.update(null, updateWrapper);
    System.out.println("===update===" + update);
}
3.3 LambdaQueryWrapper

a.例1

@Test
public void lambdaTest01() {
    // 查询用户名字包含a并且年龄大于等于ageBegin且小于等于ageEnd的信息
    // User::getName 相当于对应的数据表字段name,这种写法可以防止写错字段
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)
    String name = "a";
    Integer ageBegin = null;
    Integer ageEnd = 30;
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.like(StringUtils.isNotBlank(name), User::getName, name)
            .ge(ageBegin != null, User::getAge, ageBegin)
            .le(ageEnd != null, User::getAge, ageEnd);

    List<User> users = userMapper.selectList(queryWrapper);

    users.forEach(System.out::println);
}
3.4 LambdaUpdateWrapper

a.例子

@Test
public void updateTest02() {
    // 将用户名中包含有a并且(年龄大于25或邮箱为null)的用户信息修改
    // UPDATE user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.like(User::getName, "a")
            .and(i -> i.gt(User::getAge, 25).or().isNull(User::getEmail));
    updateWrapper.set(User::getName, "小黑").set(User::getEmail, "xiaohei@qq.com");

    int update = userMapper.update(null, updateWrapper);
    System.out.println("===update===" + update);
}

4 插件

4.1 分页插件

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

a.添加配置类

package com.yamei.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.yamei.mybatisplus.mapper") // 可以将主类中的注解移到此处
public class MybatisPlusConfig {
  @Bean
  public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
  }
}

测试例子:

@Test
public void test01() {
    // Page 接收两个参数:参数1:当前页码,参数2:每页多少条
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 LIMIT ?
    Page<User> page = new Page<>(1, 3);
    userMapper.selectPage(page, null);

    System.out.println(page.toString());
    // 总页数
    System.out.println(page.getPages());
    // 总条数
    System.out.println(page.getTotal());
}

自定义接口使用分页功能
在这里插入图片描述

a.接口定义

public interface UserMapper extends BaseMapper<User> {
  /**
   * 通过年龄查询用户信息并分页
   * 接口中使用@Param设置别名,需要到配置文件设置一下,否则报错
   * @param page mybatis-plus 所提供的分页对象,必须位于第一个参数的位置
   * @param age
   * @return
   */
  Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
}

b.SQL语句编写

<?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.yamei.mybatisplus.mapper.UserMapper">

  <!-- Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age); -->
  <select id="selectPageVo" resultType="User">
    select id,`name`,age,email from user where age > #{age}
  </select>
</mapper>

c.测试调用

@Test
public void test02() {
    Page<User> page = new Page<>(1, 3);

    userMapper.selectPageVo(page, 20);

    System.out.println(page);
}
4.2 乐观锁

a.场景

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


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


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

b.乐观锁与悲观锁

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


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

c.例子:测试修改冲突
创建一张商品表

create table t_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)
);

实体类

package com.yamei.mybatisplus.pojo;

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

@Data
@TableName("t_product")
public class Product {
  private Long id;

  private String name;

  private Integer price;

  private Integer version;
}

模拟修改冲突过程:

@Test
public void productTest01() {
    // 小李查询商品价格
    Product productLi = productMapper.selectById(1);
    System.out.println("小李查询商品价格:" + productLi.getPrice());
    // 小王查询商品价格
    Product productWang = productMapper.selectById(1);
    System.out.println("小王查询商品价格:" + productWang.getPrice());

    // 小李设置商品价格
    productLi.setPrice(productLi.getPrice() + 50);
    productMapper.updateById(productLi);
    // 小王设置商品价格
    productWang.setPrice(productWang.getPrice() - 30);
    productMapper.updateById(productWang);

    // 老板查询商品价格
    Product productBoss = productMapper.selectById(1);
    System.out.println("老板查询商品价格:" + productBoss.getPrice());
}

d.乐观锁实现流程

数据库中添加version字段


取出记录时,获取当前version


select id, `name`, price, version from t_product where id = 1


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


update t_product set price = price + 50, version = version + 1 where id = 1 and version = 1;

e.Mybatis-Plus实现乐观锁

修改实体类:添加@Version注解

@Data
@TableName("t_product")
public class Product {
  private Long id;

  private String name;

  private Integer price;

  @Version // 标识乐观锁版本号字段
  private Integer version;
}

修改config文件,添加乐观锁插件

@Configuration
//@MapperScan("com.yamei.mybatisplus.mapper") // 可以将主类中的注解移到此处
public class MybatisPlusConfig {
  @Bean
  public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // ...
    // 添加乐观锁插件
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
  }
}

模拟乐观锁测试:

@Test
public void productTest01() {
    // 小李查询商品价格
    Product productLi = productMapper.selectById(1);
    System.out.println("小李查询商品价格:" + productLi.getPrice());
    // 小王查询商品价格
    Product productWang = productMapper.selectById(1);
    System.out.println("小王查询商品价格:" + productWang.getPrice());

    // 小李设置商品价格
    productLi.setPrice(productLi.getPrice() + 50);
    productMapper.updateById(productLi);
    // 小王设置商品价格
    productWang.setPrice(productWang.getPrice() - 30);
    int result = productMapper.updateById(productWang);

    if (result == 0) {
      // 更新失败则重试
      Product productNew = productMapper.selectById(1);
      productNew.setPrice(productNew.getPrice() - 30);
      productMapper.updateById(productNew);
    }

    // 老板查询商品价格
    Product productBoss = productMapper.selectById(1);
    System.out.println("老板查询商品价格:" + productBoss.getPrice());
}

5 通用枚举

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

a.数据库表添加字段sex
在这里插入图片描述

b.新建枚举类以及修改实体类

package com.yamei.mybatisplus.enums;

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

@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;
  }
}
package com.yamei.mybatisplus.pojo;

import com.baomidou.mybatisplus.annotation.*;
import com.yamei.mybatisplus.enums.SexEnum;
import lombok.Data;

@Data
public class User {
  private String name;

  private Integer age;

  private String email;
  
  // 添加sex 类型为枚举类
  private SexEnum sex;

  @TableLogic
  private Integer isDeleted;
}

c.修改application.yml配置文件

添加以下配置

mybatis-plus:
  # ...
  # 扫描通用枚举的包
  type-enums-package: com.yamei.mybatisplus.enums

d.测试

@Test
public void testEnum01() {
    // INSERT INTO user ( id, name, age, email, sex ) VALUES ( ?, ?, ?, ?, ? )
    User user = new User();
    user.setName("enum1");
    user.setAge(22);
    user.setEmail("123456@qq.com");
    user.setSex(SexEnum.MALE);

    userMapper.insert(user);
}

6 代码生成器

6.1 引入依赖
<!-- 引入代码生成器 -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-generator</artifactId>
  <version>3.5.1</version>
</dependency>
<dependency>
  <groupId>org.freemarker</groupId>
  <artifactId>freemarker</artifactId>
  <version>2.3.31</version>
</dependency>
6.2 快速生成代码
package com.yamei.mybatisplus;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.sql.Types;
import java.util.Collections;

public class FastAutoGeneratorTest {
  public static void main(String[] args) {
    FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true", "root", "root")
            .globalConfig(builder -> {
              builder.author("yamei") // 设置作者
//                      .enableSwagger() // 开启 swagger 模式
                      .fileOverride() // 覆盖已生成文件
                      .outputDir("D://mybatis_plus"); // 指定输出目录
            })
            .packageConfig(builder -> {
              builder.parent("com.yamei") // 设置父包名
                      .moduleName("mybatisplus") // 设置父包模块名 // parent和moduleName 意思是生成的代码都在com.yamei.mybatisplus 目录下
                      .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus")); // 设置mapperXml 映射文件生成路径
            })
            .strategyConfig(builder -> {
              builder.addInclude("t_menu") // 设置需要生成的表名
                      .addTablePrefix("t_", "c_"); // 设置过滤表前缀
            })
            .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
            .execute();
  }
}

生成结果:具体内容自己生成查看
在这里插入图片描述

7 多数据源

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


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


场景说明:


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

7.1 创建数据库以及表

创建数据库mybatis_plus_1以及product表


create database `mybatis_plus_1`;

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库的t_product 表

use mybatis_plus;
drop table if exists t_product;
7.2 引入依赖
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>3.5.0</version>
</dependency>
7.3 配置多数据源

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

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

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 配置数据库表名前缀,这样实体类名就可以跟表名不一致:如表t_user --> 实体类: User
  #global-config:
    #db-config:
      #table-prefix: t_
  type-aliases-package: com.yamei.mybatisplus.pojo
  # 扫描通用枚举的包
  type-enums-package: com.yamei.mybatisplus.enums
7.4 创建用户service
public interface UserService extends Iservice<User> {
}
@DS("master") // 指定所操作的数据源
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
7.5 创建商品service
public interface ProductService extends IService<Product> {
}
@DS("slave_1")
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}
7.6 测试
 @Test
 public void test01() {
    System.out.println(userService.getById(1));
    System.out.println("===================================");
    System.out.println(productService.getById(1));
 }

在这里插入图片描述

8 MyBatisX 插件

Mybatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率。但是在真正的开发过程中,Mybatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表联查等,我们该如何快速解决这个问题呢?这个时候可以使用MyBatisX插件。


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

MyBatisX插件的具体用法:https://baomidou.com/pages/ba5b24/

下面我们介绍一下MyBatisX的简单使用

8.1 安装MyBatisX插件

如果你的IDEA编辑工具还没有安装MyBatisX插件,那么请打开file->settings,找到plugins,搜索并安装该插件,然后重启编辑器即可。(注意:该插件目前兼容idea2019.3以上版本)

在这里插入图片描述

8.2 使用MyBatisX快速生成代码
  • 步骤1:在idea上连接项目使用的数据库
    在这里插入图片描述

在这里插入图片描述

  • 步骤2:选择相关表,右键选择
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这样我们就生成了基本的代码了。

8.3 使用示例

我们进入UserMapper.java文件

输入你的方法,mybatisx会根据你输入的显示相关方法(前提是mybatisx有)
在这里插入图片描述

然后选择方法,alt + enter,如下图:选中第一个,mybatisx会帮你补全接口并在对应的mapper.xml文件中生成SQL语句
在这里插入图片描述
这就是一个简单的使用例子,具体的可以去参考官网。

有什么不合理的地方请指出,谢谢!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值