一文通关MyBatisPlus

news2024/12/26 21:21:47

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉
🍎个人主页:Leo的博客
💞当前专栏: MyBatis
✨特色专栏: MySQL学习
🥭本文内容:一文通关MyBatisPlus
🖥️个人小站 :个人博客,欢迎大家访问
📚个人知识库: 知识库,欢迎大家访问

大家好,我是Leo😀,之前自学那会有简单了解过MyBatisPlus相关文档,但是随着时间长不用,一些api以及配置都忘了,想翻笔记发现当时的自己只是囫囵吞枣的学习了,任何学习记录都没有留下来,😅😅😅,所以大家可千万不要学我哈! 这不最近打算重新学习一下MyBatisPlus(下文简称MP),这里也是写篇博客来整理一下相关知识点,与君共勉🫡🫡🫡!

话不多说,我们先来看一张简单的思维导图,了解一下MP的主要核心功能,脑图在手,思路我有,开干🤔!

image-20230831113008757

1. 快速开始

1.1 介绍

在快速开始我们入门Demo之前,我来首先简单介绍一下 MP

官网: https://baomidou.com/
简介:MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

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

image-20230831113906219

特性:

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

  2. 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

  3. 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分

    CRUD 操作,更有强大的条件构造器,满足各类使用需求

  4. 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

  5. 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题

  6. 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作

  7. 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

  8. 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用

  9. 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询

  10. 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库

  11. 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询

  12. 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操

1.2 框架设计

image-20230831114321021

1.3 快速入门MP

我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,你需要学会:

  • Java 开发环境以及相应 IDE
  • 熟悉 Spring Boot
  • 熟悉 Maven

1. 创建表结构

现有一张 User 表,其表结构如下:

idnameageemialaddress
1admin20admin@qq.com湖北武汉
3test20test@qq.com安徽合肥
4zhangsan17zhangsan@qq.com北京海淀

其对应的数据库 Schema 脚本如下:

DROP TABLE IF EXISTS user;
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

对应的数据库 Data 脚本如下:

DELETE FROM user;

INSERT INTO user (id, name, age, email,address) VALUES
(1, 'admin', 18, 'test1@admin.com''湖北武汉'),
(3, 'test', 20, 'test2@test.com''湖北武汉'),
(4, 'zhangsan', 21, 'test4@zhangsan.com''湖北武汉'),

2. 创建SpringBoot工程

本次教程会以Maven工程创建修改为SpringBoot工程。

我这里是在父工程下面创建许多子模块,这样就不需要每一个新的demo都需要去创建一个新的工程了,也方便Demo项目统一管理。

image-20230831115942637

3. 添加依赖

引入 SpringBoot Starter 父工程:

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
    </parent>

引入 spring-boot-starterspring-boot-starter-testmybatis-plus-boot-starter、MySQL依赖:

<dependencies>
        <!--spring-boot-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--SpringBootTest-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--mysql 驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
    </dependencies>

4. 配置

application.yml 配置文件中添加 MySQL数据库的相关配置:

server:
  port: 8800
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
    username: root
    password: root
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
  mapper-locations: classpath:mapper/*.xml

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

package com.Leo.mp;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

/**
 * @author : Leo
 * @version 1.0
 * @date 2023/8/31 13:26
 * @description : 启动类
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@MapperScan("com.Leo.mp.mapper")
public class Application
{
    public static void main(String[] args)
    {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        Environment environment = context.getBean(Environment.class);
        System.out.println("访问链接:http://localhost:" + environment.getProperty("server.port"));
        System.out.println("(♥◠‿◠)ノ゙  项目启动成功   ლ(´ڡ`ლ)゙  \n" +
                " .-------.       ____     __        \n" +
                " |  _ _   \\      \\   \\   /  /    \n" +
                " | ( ' )  |       \\  _. /  '       \n" +
                " |(_ o _) /        _( )_ .'         \n" +
                " | (_,_).' __  ___(_ o _)'          \n" +
                " |  |\\ \\  |  ||   |(_,_)'         \n" +
                " |  | \\ `'   /|   `-'  /           \n" +
                " |  |  \\    /  \\      /           \n" +
                " ''-'   `'-'    `-..-'              ");
    }
}

5. 编码

编写实体类 User.java(此处使用了 Lombok (opens new window)简化代码)

package com.Leo.mp.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author : Leo
 * @version 1.0
 * @date 2023/8/31 13:32
 * @description : User实体类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private String address;
}

编写 Mapper 包下的 UserMapper接口

/**
 * @author : Leo
 * @version 1.0
 * @date 2023/8/31 13:33
 * @description :
 */
public interface UserMapper extends BaseMapper<User> {

}

6. 测试

以上工作都做完就可以开始进行功能测试

package com.Leo.mp.controller;

import com.Leo.mp.mapper.UserMapper;
import com.Leo.mp.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author : Leo
 * @version 1.0
 * @date 2023/8/31 13:43
 * @description :
 */
@RestController
public class UserController
{

    @Autowired
    private UserMapper userMapper;

    @GetMapping("/list")
    public List<User> userList()
    {
        return userMapper.selectList(null);
    }

}

提示

UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件

启动项目,打开浏览器

image-20230831134539746

7. 小结

通过以上几个简单的步骤,我们就实现了 User 表的 查询 功能,甚至连 XML 文件都不用编写!

从以上步骤中,我们可以看到集成MyBatis-Plus非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。

MP 的强大远不止这些功能,想要详细了解 那就继续往下看吧!

2. 常见注解

本文将介绍 MybatisPlus 注解包相关类详解(更多详细描述可点击查看源码注释)

2.1 @TableName

  • 描述:表名注解,标识实体类对应的表, 通俗来说就是让数据库表名和实体类一一对应
  • 使用位置:实体类
@TableName("user")
public class User {
    private Long id;
    private String  name;
    private Integer age;
    private String  email;
    private String  address;
}

image-20230831134901251

关于 autoResultMap 的说明:

MP 会自动构建一个 resultMap 并注入到 MyBatis 里(一般用不上),请注意以下内容:

因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 实体类字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。

2.2 @TableId

public class User {
    @TableId
    private Long id;
    private String  name;
    private Integer age;
    private String  email;
    private String  address;
}

image-20230831141456740

IdType

image-20230831135246510

2.3 @TableField

  • 描述:字段注解(非主键)
public class User {
    private Long id;
    @TableField("username")
    private String  name;
    private Integer age;
    private String  email;
    private String  address;
}

我们可以通过Ctrl + 鼠标左键点进去看看

image-20230831140046119

image-20230831135918882

2.4 FieldFill

image-20230831140158881

2.5 乐观锁

描述:乐观锁注解、标记 @Version 在字段

2.6 @TableLogic

描述:表字段逻辑处理注解(逻辑删除)

image-20230831140301725

2.7 @OrderBy

描述:内置 SQL 默认指定排序,优先级低于 wrapper 条件查询

image-20230831140357839

3. 核心功能

3.1 主键策略

MP默认实现5种主键生成策略,分别是:

  • AUTO : 配合数据库设置自增主键,可以实现主键的自动增长,类型为 nmber
  • INPUT : 由用户输入。
  • NONE : 不设置,等同于INPUT
  • ASSIGN_ID :只有当用户未输入时,采用雪花算法生成一个适用于分布式环境的全局唯一主键,类型可以是Stringnumber
  • ASSIGN_UUID :只有当用户未输入时,生成一个String类型的主键,但不保证全局唯一。

IdType默认的全局设置为IdType.ASSIGN_ID,即由mybatis-plus主动分配主键,默认情况下由默认主键生成器实现类DefaultIdentifierGenerator采用雪花算法填充主键。

public DbConfig() {
    this.idType = IdType.ASSIGN_ID;
    this.tableUnderline = true;
    this.capitalMode = false;
    this.logicDeleteValue = "1";
    this.logicNotDeleteValue = "0";
    this.insertStrategy = FieldStrategy.NOT_NULL;
    this.updateStrategy = FieldStrategy.NOT_NULL;
    this.whereStrategy = FieldStrategy.NOT_NULL;
}

SpringBoot中,可以通过如下配置更改全局配置。

mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id

3.2 条件构造器

说明:

  • 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)
  • 以下代码块内的多个方法均为从上往下补全个别boolean类型的入参,默认为true
  • 以下出现的泛型Param均为Wrapper的子类实例(均具有AbstractWrapper的所有方法)
  • 以下方法在入参中出现的R为泛型,在普通wrapper中是String,在LambdaWrapper中是 函数 (例:Entity::getId,Entity为实体类,getId为字段id的 getter Method )
  • 以下方法入参中的R column均表示数据库字段,当R具体类型为String时则为数据库字段名( 字段名是数据库关键字的自己用转义符包裹 )!而不是实体类数据字段名!!!,另当R具体类型为SFunction时项目runtime不支持eclipse自家的编译器!!!

下面看一图来简单了解一下Wrapper的结构

1. Wrapper

条件构造抽象类,最顶端父类,抽象类中提供3个方法以及其他方法

image-20230831143422871

2. AbstractWrapper

用于查询条件封装,生成 sql 的 where 条件,QueryWrapper(LambdaQueryWrapper) 和UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where条件

image-20230831143514543

这里列举一下他的重要方法

image-20230831143553299

3. AbstractLambdaWrapper

Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。

4. LambdaQueryWrapper

用于Lambda语法使用的查询Wrapper

5. LambdaUpdateWrapper

Lambda 更新封装Wrapper

6. QueryWrapper

实体类 对象封装操作类,不是用lambda语法,自身的内部属性 实体类 也用于生成 where 条件

image-20230831143717067

7. UpdateWrapper

说明:

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

set
set(String column, Object val)
set(boolean condition, String column, Object val)
  • SQL SET 字段
  • 例: set("name", "Leo")
  • 例: set("name", "")—>数据库字段值变为空字符串
  • 例: set("name", null)—>数据库字段值变为null
setSql
setSql(String sql)
  • 设置 SET 部分 SQL
  • 例: setSql("name = 'Leo'")
lambda
  • 获取 LambdaWrapper
    QueryWrapper中是获取LambdaQueryWrapper
    UpdateWrapper中是获取LambdaUpdateWrapper

3.3 CRUD接口

1. 带条件查询

// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

2. 带条件更新

	UpdateWrapper<Student> updateWrapper = new UpdateWrapper<Student>();
	updateWrapper.eq("name", "Leo").eq("age", 18).set("id", 100);
	empolyeeMapper.update(student, updateWrapper);

3. 带条件删除

// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

4. 实战案例

package com.Leo.mp;

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

import java.util.List;
import java.util.Map;

/**
 * @author : Leo
 * @version 1.0
 * @date 2023/8/31 13:36
 * @description :
 */
@SpringBootTest
public class MpTest {

    @Autowired
    private UserMapper userMapper;

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

    @Test
    void test1() {
        // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于17
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .isNotNull("name")
                .ge("age",17);
        userMapper.selectList(wrapper).forEach(System.out::println);
    }

    @Test
    void test2(){
        // 查询名字admin
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .eq("name","admin");
        User user = userMapper.selectOne(wrapper); // 查询一个数据,出现多个结果使用List 或者 Map
        System.out.println(user);
    }

    @Test
    void test3(){
        // 查询年龄在 20 ~ 30 岁之间的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age",20,30); // 区间
        Long count = userMapper.selectCount(wrapper);// 查询结果数
        System.out.println(count);
    }

    // 模糊查询
    @Test
    void test4(){
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
        // 左和右  t%
                .notLike("name","ad")
                .likeRight("email","@qq.com");
        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
    }

    // 模糊查询
    @Test
    void test5(){

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // id 在子查询中查出来
        wrapper.inSql("id","select id from student where id<3");

        List<Object> objects = userMapper.selectObjs(wrapper);
        objects.forEach(System.out::println);
    }

    //测试六
    @Test
    void test6(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // 通过id进行排序
        wrapper.orderByAsc("id");
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
}

3.4 代码生成器

对于单表的操作,虽然MP大大简化了操作,但是每次都需要我们重新创建文件,从mapper到controller,那将会带来太大的工作量。

于是我们选择使用MP为我们带来的新版生成器,一劳永逸🥳

注意

适用版本:mybatis-plus-generator 3.5.1 及其以上版本,对历史版本不兼容!

目前支持两套生成的方式,一套使用SQL查询的方式是兼容旧的代码生成器核心逻辑使用,另一套使用驱动规范来读取元数据的方式,默认的使用元数据查询方式来生成代码

快速入门

导入依赖

<!--mybatis-plus-generator 生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--velocity-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>
        <!--以下两个引擎模板保留一个即可,看个人爱好选择-->
        <!--freemarker引擎模板-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>
        <!--beetl引擎模板-->
        <dependency>
            <groupId>com.ibeetl</groupId>
            <artifactId>beetl</artifactId>
            <version>3.8.1.RELEASE</version>
        </dependency>

快速生成

这里我已经根据官网配置生成好了一个通用的MP工具类,大家可以直接拿来用,所见即所得。

package com.cisyam.code.util;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author : gaoziman
 * @description : MP 代码生成器
 * @date 2023/7/19 20:01
 */
public class CodeUtil {
    public static void main(String[] args) {

        //这里按着给的注解修改参数即可

        //手动配置数据源

        //注意修改数据库名
        String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8";
        String name = "root";
        String password = "root";

        //数据库表的设置

        //设置需要自动代码生成的表名

        List<String> listTable = Arrays.asList("employee", "user");

        //设置 过滤 表的后缀

        List<String> listTableSuffix = Collections.singletonList("_b");

        //设置 过滤 表的后缀

        List<String> listTablePrefix = Arrays.asList("t_", "c_");


        //基本信息
        String author = "gaoziman";
        //作者
        String parent = "com.cisyam";
        //父包名
        String module = "code";
        //模块包名


        //已封装好,无需更改。可按照需求进行注释

        //1、配置数据源
        FastAutoGenerator.create(url, name, password)
                //2、全局配置
                .globalConfig(builder -> {
                    builder.author(author) // 设置作者名
                            .outputDir(System.getProperty("user.dir") + "/src/main/java")   //设置输出路径:项目的 java 目录下【System.getProperty("user.dir")意思是获取到项目所在的绝对路径】
                            .commentDate("yyyy-MM-dd hh:mm:ss")   //注释日期
                            .dateType(DateType.ONLY_DATE)   //定义生成的实体类中日期的类型 TIME_PACK=LocalDateTime;ONLY_DATE=Date;
                            .fileOverride()   //覆盖之前的文件
                            .enableSwagger()   //开启 swagger 模式
                            .disableOpenDir();   //禁止打开输出目录,默认打开
                })
                //3、包配置
                .packageConfig(builder -> {
                    builder.parent(parent) // 设置父包名
                            .moduleName(module)   //设置模块包名
                            .entity("pojo")   //pojo 实体类包名
                            .service("service") //Service 包名
                            .serviceImpl("service.impl") // ***ServiceImpl 包名
                            .mapper("mapper")   //Mapper 包名
                            .xml("mapper.xml")  //Mapper XML 包名
                            .controller("controller") //Controller 包名
                            .other("config")    //自定义包名(一般不在这里生成,而是后面编写的时候自己建包)
                            .pathInfo(Collections.singletonMap(OutputFile .xml, System.getProperty("user.dir") + "/src/main/resources/mapper"));    //配置 mapper.xml 路径信息:项目的 resources 目录下
                })
                //4、策略配置
                .strategyConfig(builder -> {
                    builder
                            .enableCapitalMode()    //开启大写命名
                            .enableSkipView()   //创建实体类的时候跳过视图
                            .addInclude(listTable) // 设置需要生成的数据表名
                            .addTableSuffix(listTableSuffix) //设置 过滤 表的后缀
                            .addTablePrefix(listTablePrefix) // 设置 过滤 表的前缀

                            //4.1、实体类策略配置
                            .entityBuilder()
                            .enableChainModel() //开启链式模型
                            //.disableSerialVersionUID()  //默认是开启实体类序列化,可以手动disable使它不序列化。由于项目中需要使用序列化就按照默认开启了
                            .enableTableFieldAnnotation()       // 开启生成实体时生成字段注解
                            .enableLombok() //开启 Lombok
                            .versionColumnName("version")   //乐观锁字段名(数据库)
                            .versionPropertyName("version") //乐观锁属性名(实体)
                            .logicDeleteColumnName("deleted")   //逻辑删除字段名(数据库)
                            .logicDeletePropertyName("deleteFlag")  //逻辑删除属性名(实体)
                            .naming(NamingStrategy.underline_to_camel)  //数据库表映射到实体的命名策略:默认是下划线转驼峰命。这里可以不设置
                            .columnNaming(NamingStrategy.underline_to_camel)    //数据库表字段映射到实体的命名策略:下划线转驼峰命。(默认是和naming一致,所以也可以不设置)
                            .addTableFills(
                                    new Column("create_time", FieldFill.INSERT),
                                    new Column("modify_time", FieldFill.INSERT_UPDATE)
                            )   //添加表字段填充,"create_time"字段自动填充为插入时间,"modify_time"字段自动填充为插入修改时间
                            .idType(IdType.AUTO)    //设置主键自增

                            //4.2、Controller策略配置
                            .controllerBuilder()
                            .enableHyphenStyle()    //开启驼峰连转字符
                            .formatFileName("%sController") //格式化 Controller 类文件名称,%s进行匹配表名,如 UserController
                            .enableRestStyle()  //开启生成 @RestController 控制器

                            //4.3、service 策略配置
                            .serviceBuilder()
                            .formatServiceFileName("%sService") //格式化 service 接口文件名称,%s进行匹配表名,如 UserService
                            .formatServiceImplFileName("%sServiceImpl") //格式化 service 实现类文件名称,%s进行匹配表名,如 UserServiceImpl

                            //4.4、Mapper策略配置
                            .mapperBuilder()
                            .superClass(BaseMapper.class)   //设置父类
                            .enableBaseResultMap()  //启用 BaseResultMap 生成
                            .enableBaseColumnList() //启用 BaseColumnList
                            .formatMapperFileName("%sMapper")   //格式化 mapper 文件名称
                            .enableMapperAnnotation()       //开启 @Mapper 注解
                            .formatXmlFileName("%sMapper") //格式化Xml文件名称
                            .formatMapperFileName("%sMapper");   //格式化Mapper文件名称

                })
                //5、模板
                .templateEngine(new VelocityTemplateEngine())
                /*
                    模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker(以下两个引擎用哪个就保留哪个)
                   .templateEngine(new BeetlTemplateEngine())
                   .templateEngine(new FreemarkerTemplateEngine())
                 */
                .templateEngine(new FreemarkerTemplateEngine())
                //本人选择了Freemarker
                //6、执行
                .execute();
    }
}

4. 拓展

4.1 全局ID生成策略

配置了之后就不需要在实体类主键上配置了

mybatis-plus:
  global‐config:
    db‐config:
      id‐type: auto

4.2 逻辑删除

物理删除: 在删除的时候直接将数据从数据库干掉DELTE
逻辑删除: 从逻辑层面控制删除,通常会在表里添加一个逻辑删除的字段比如 enabled 、is_deleted ,数据默认是有效的(值为1),当用户删除时将数据修改UPDATE 0, 在查询的时候就只查 where is_deleted =1.

在阿里开发规范中,当我们设计数据库字段表示有 ” 是 否“ 相关概念是,应该是设置为is_xxxx 类型。

需要添加逻辑删除的字段
局部单表逻辑删除,需要在对应的pojo类加入对应的逻辑删除标识字段

@TableLogic // 代表逻辑删除(单个字段的)
private Integer is_deleted;

开启全局逻辑删除配置,如果进行了全局逻辑删除配置并且指定了,就可以不用在每个实体类中配置了@TableLogic

mybatis-plus:
  global‐config:
    db-config:
      id‐type: auto
      logic-delete-field: is_deletd # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

当执行删除, 将会把逻辑删除字段进行修改,执行的sql语句为

update user set is_deletd = 0 where id= ? 

当执行查询时。会自动查询有效数据 where flag=1

select id,name,age,email,address from user where is_deleted=1

4.3 分页插件

1. PaginationInnerInterceptor

支持的数据库
  • mysql,oracle,db2,h2,hsql,sqlite,postgresql,sqlserver,Phoenix,Gauss ,clickhouse,Sybase,OceanBase,Firebird,cubrid,goldilocks,csiidb,informix,TDengine,redshift
  • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库,优炫数据库,星瑞格数据库
属性介绍

image-20230831153549029

2. Page

该类继承了 IPage 类,实现了 简单分页模型 如果你要实现自己的分页模型可以继承 Page 类或者实现 IPage

image-20230831153516852

3. 配置类

要使用MP的分页插件,必须配置一个全局配置类,才可以全局生效。下面我就直接放代码,大家可以直接拿来用。

package com.manman.common.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
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;

/**
 * @author Loe
 */
@Configuration
@MapperScan("com.Leo.mp.mapper")
public class MybatisPlusConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

4.4 乐观锁插件

1. 介绍

悲观锁:

悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。
假设功能并发量非常大,就需要使用 synchronized 来处理高并发下产生线程不安全问题, 会使其他线程进行挂起等待从而影响系统吞吐量

乐观锁:

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。
假设功能产生并发几率极少,采用乐观锁版本机制对比, 如果有冲突 返回给用户错误的信息

2. 为什么需要锁

在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题

丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户1把值从500改为8000,用户B把值从500改为200,则多人同时提交同一条记录,后提交的把之前的提交数据覆盖。
脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读。例如:用户A,B看到的值都是500,用户B把值改为200,用户A读到的值仍为500。

针对一种问题的解决方案,为解决问题而生的。解决什么问题呢?主要是解决丢失更新问题如下图理解

image-20230831152857083

为了解决这些并发带来的问题。 我们需要引入并发控制机制

3. 编码实现

image-20230831153117810

  1. 在对应的实体类中加version字段,并设置成下面这样

    @Version //这就是控制版本的
    @TableField(fill = FieldFill.INSERT) //这个方便在添加的时候设置版本初始为1
    private Integer version; //版本的字段
    
    1. 创建一个新的类,实现MetaObjectHandler自动填充,像创建时间,更新时间也可以在这操作。
    @Component
    public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
    	@Override
    	public void insertFill(MetaObject metaObject) {
    	//这里的“version”就是指定的字段,设置初始值为1,之后每修改一次+1 
    		this.setFieldValByName("version",1,metaObject);
    	}
    	@Override
    	public void updateFill(MetaObject metaObject) {
    	}
    }
    
  2. 在创建一个配置类,开启一个乐观锁插件

    @Configuration
    @MapperScan("com.zhz.mapper")//这里就是你的mapper文件的包
    public class MyBatisConfig {
    	//乐观锁插件
    	@Bean
    	public OptimisticLockerInterceptor optimisticLockerInterceptor(){
    		return new OptimisticLockerInterceptor();
    	}
    }
    

接下来在做增加数据的时候,调用 insert 添加方法就可以了。修改的时候呢,我们需要先查人后再做修改,因为我们为了防止问题的发生,需要先去查询版本号比对才进行后续操作!!

5. 总结

以上便是关于MP相关的内容,本人才疏学浅,文章有什么错误的地方,欢迎大佬们批评指正!我是Leo,一个在互联网行业的小白,立志成为更好的自己。

如果你想了解更多关于Leo,可以关注下面这个公众号,后面文章会首先同步至公众号。

扫码_搜索联合传播样式-标准色版

参考资料:

MP官网

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/954094.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssm园区停车管理系统源码和论文

ssm园区停车管理系统源码和论文104 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#x…

keytool生成证书

配置jdk 下载jdk https://www.oracle.com/java/technologies/downloads/#java8-linux 解压文件 tar -xvf jdk-8u381-linux-x64.tar.gz配置环境变量 export PATH$PATH:/root/jdk1.8.0_381/bin注意&#xff1a;此处仅配置了建议环境变量&#xff0c;若需要配置jdk完整环境变量&…

Blender 围绕自身的原点旋转与游标旋转

默认情况下的旋转是&#xff0c;R后旋转是物体自身的原点旋转 可以修改为围绕游标旋转&#xff0c;通过旋转R时 局部与全局坐标 全局的坐标不会变 局部的会随着物体的旋转变化 如果平稳时GZZ会在全局到局部坐标之间切换 或在局部到全局之间的切换 学习视频&#xff1a;【基础…

OCR文字检测与识别系统:融合文字检测、文字识别和方向分类器的综合解决方案

1. PP-OCR系统简介与总览 前两章主要介绍了DBNet文字检测算法以及CRNN文字识别算法。然而对于我们实际场景中的一张图像&#xff0c;想要单独基于文字检测或者识别模型&#xff0c;是无法同时获取文字位置与文字内容的&#xff0c;因此&#xff0c;我们将文字检测算法以及文字…

算法通关村第9关【黄金】| 两道有挑战的问题

1. 将有序数组转换为二叉搜索树 思路&#xff1a;二分法&#xff0c;这个算法保证了每次选择的中间元素都能保持左右子树的高度差不超过 1&#xff0c;从而构建一个高度平衡的二叉搜索树。这个过程类似于分治法&#xff0c;通过递归不断将大问题分解成小问题并解决。 找到数组…

和24考研说拜拜,不考研读中外合作办学硕士——人大女王金融硕士

23考研失利同学&#xff0c;大多都会有这样的疑虑&#xff0c;是再试一次还是选择其他方式呢&#xff1f;其实&#xff0c;并不用执着于全国联考&#xff0c;中外合作办学硕士或许更适合你。近年来&#xff0c;经济迅速发展&#xff0c;经济全球化不断扩大&#xff0c;金融方向…

怪兽充电连续两季度盈利,共享充电宝又行了?

智能手机的时代&#xff0c;“电量焦虑”是一个从未得到完美解决的问题。 彼得戴曼迪斯在《创业无畏》中写下&#xff1a;“世界上最大的问题等于最大的商机。” 近期&#xff0c;科技消费公司怪兽充电发布了2023年第二季度业绩报告。根据财报&#xff0c;怪兽充电第二季度实…

云计算中的负载均衡技术,确保资源的平衡分配

文章目录 1. 硬件负载均衡器2. 软件负载均衡器3. DNS负载均衡4. 内容分发网络&#xff08;CDN&#xff09; &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&#xff1a;云计算 ✨文章内…

【锁】定时任务推送数据-redission加锁实例优化

文章目录 redission 加锁代码-有问题优化代码看门狗是什么&#xff1f; redission 加锁代码-有问题 /*** 收货入库物料标签(包装码)推送接口** throws Exception*/public void synReceiveMaterialTags() throws Exception {String tag DateFormatUtils.format(new Date(), &qu…

操作系统中一些零散的知识点

第三章 内存管理 在虚拟内存系统中&#xff0c;虚拟内存的最大容量是由计算机的地址结构&#xff08;CPU寻址范围&#xff09;确定的&#xff0c;而虚拟内存的实际容量是受到“内存大小磁盘空间大小”、“地址线位数”共同制约&#xff0c;取二者最小值实现虚拟内存管理必须有…

手把手教你实现一个可编辑的Table

手把手教你实现一个可编辑的Table 需求背景 我们最近在做一些商品的备案工作&#xff0c;历史有很多的备案记录&#xff0c;不能很好的利用。所以想做一个提效工具。 备案人员&#xff0c;在网页通过搜索历史知识库的数据&#xff0c;进行备案编辑&#xff0c;他们平时都在使用…

亚马逊美国站上半年“日本商店”畅销品类了解一下吧!

近日&#xff0c;亚马逊美国站公布了2023年上半年“日本商店”&#xff08;JAPAN STORE&#xff09;的热门品类以及各品类销量排名前三的商品。据了解&#xff0c;亚马逊与日本贸易振兴机构 (JETRO) 合作&#xff0c;于2021年11月推出“日本商店”&#xff0c;支持日本卖家出海…

Vue安装过程的困惑解答——nodejs和vue关系、vue的项目结构

文章目录 一、为什么在使用vue前要下载nodejs&#xff1f;二、为什么安装nodejs后就能使用NPM包管理工具&#xff1f;三、为什么是V8引擎并且使用C实现&#xff1f;四、为什么会安装淘宝镜像&#xff1f;五、什么是webpack模板&#xff0c;为什么需要他&#xff1f;六、vue项目…

2023年中国电商销售额将达2.2万亿美元领跑全球

根据Global Data的数据&#xff0c;中国将继续保持在全球电子商务格局中的主导地位。预计中国电子商务市场将增长9.9%&#xff0c;到2023年将增长22亿美元。 2018年至2022年间&#xff0c;中国的在线销售额复合年增长率为11.2%。电商销售额在2022年达到了2万亿美元。预计2023年…

Python 实践之Pandas 时间数据处理方法详解

概要 表格数据中常见的数据类型是数值型&#xff08;包括整数、浮点数&#xff09;和字符型&#xff0c;除了这两种数据&#xff0c;时间类型数据也是常见数据的重要组成部分&#xff0c;同时也是数据分析中极其重要的信息。无论是金融领域的股票交易数据&#xff0c;还是企业注…

恒运资本:a股总市值怎么看?

跟着经济开展和金融商场的壮大&#xff0c;投资者越来越重视股市的行情。而对于A股商场来说&#xff0c;总市值是一个非常重要的目标。那么&#xff0c;A股总市值怎么看&#xff1f;以下是本文的详细剖析。 一、A股总市值界说与核算方法 首要需要了解A股总市值的界说和核算方法…

深度了解BSP452芯片特性与应用|百能云芯

在现代科技领域&#xff0c;芯片是推动各种电子设备发展的核心。BSP452作为一款重要的芯片&#xff0c;具有广泛的应用领域和独特的特性&#xff0c;为多个领域的创新提供了支持。百能云芯将深入探讨BSP452芯片的特点、应用领域以及未来发展前景。 BSP452芯片是一款N沟道MOS场效…

详细对比超融合服务器硬件平滑升级方案:新建集群 VS 滚动升级

作者&#xff1a;深耕行业的金融团队 刘慧敏 在企业 IT 基础架构运维中&#xff0c;经常会遇到以下问题&#xff0c;从而需要对服务器硬件进行更换或升级&#xff1a; 服务器达到维护期限&#xff1a;通常在金融行业中&#xff0c;生产环境的服务器维护期限在 5 年左右&#…

【进程间通信】管道

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;数据结构&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;网络编程等领域UP&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff0…

基于RabbitMQ的模拟消息队列需求文档

文章目录 一、项目背景二、需求分析1.核心概念2.BrokerServer核心组件3.核心API4.交换机类型5.持久化6.网络通信7.消息应答 三、消息队列模块划分 一、项目背景 什么是消息队列&#xff1f; 消息队列就是&#xff0c;基于阻塞队列&#xff0c;封装成一个独立的服务器程序&#…