【MybatisPlus快速入门】—— 拓展入门

news2025/6/19 8:08:20

逻辑删除

  • 前面我们完成了基本的增删改查操作,但是对于删除操作来说,我们思考一个问题,在实际开发中我们真的会将数据完成从数据库中删除掉么?
  • 很多情况下我们是需要保留要删除的数据用来总结统计的,所以我们是不能将数据真实删除的,这里我们就采取逻辑删除的方案
  • 逻辑删除的操作就是增加一个字段表示这个数据的状态,如果一条数据需要删除,我们通过改变这条数据的状态来实现,这样既可以表示这条数据是删除的状态,又保留了数据以便以后统计

1️⃣ 先在表中增加一列字段,表示是否删除的状态,这里我们使用的字段类型为int类型【通过1表示该条数据可用,0表示该条数据不可用】

在这里插入图片描述

2️⃣ 实体类添加一个字段为Integer,用于对应表中的字段

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User extends Model<User> {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    @TableLogic(value = "1",delval = "0")
    private Integer status;
}

3️⃣ 测试逻辑删除效果

@Test
void logicDelete(){
    userMapper.deleteById(7L);
}
  • 查看拼接的SQL语句,我们发现在执行删除操作的时候,语句变成了修改,是将这条数据的状态由1变为的0,表示这条数据为删除状态
    在这里插入图片描述

  • 我们还可以通过全局配置来实现逻辑删除的效果
    在这里插入图片描述

通用枚举

  • 首先我们先来回顾一下枚举,什么是枚举呢?
    • 当我们想要表示一组信息,这组信息只能从一些固定的值中进行选择,不能随意写,在这种场景下,枚举就非常的合适。
    • 例如我们想要表示性别,性别只有两个值,要么是男性,要么是女性,那我们就可以使用枚举来描述性别。

1️⃣ 我们先在表中添加一个字段,表示性别,这里我们一般使用int来描述,因为int类型可以通过0和1这两个值来表示两个不同的性别
在这里插入图片描述

2️⃣ 编写枚举类

public enum GenderEnum {

    MAN(0,"男"),
    WOMAN(1,"女");

    private Integer gender;
    private String genderName;

    GenderEnum(Integer gender, String genderName) {
        this.gender = gender;
        this.genderName = genderName;
    }
}

3️⃣ 实体类添加相关字段

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User extends Model<User> {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private GenderEnum gender;
    private Integer status;
}

4️⃣ 添加数据

@Test
void enumTest(){
    User user = new User();
    user.setName("liu");
    user.setAge(29);
    user.setEmail("liu@powernode.com");
    user.setGenderEnum(GenderEnum.MAN);
    user.setStatus(1);
    userMapper.insert(user);
}

此时我们查看控制台,会发现添加失败了
在这里插入图片描述

  • 原因是我们无法将一个枚举类型作为int数字插入到数据库中。不过我们对于枚举类型都给了对应的int的值
  • 所以这里我们只需要进行一个配置,就可以将枚举类型作为数字插入到数据库中,为属性gender,添加上 @EnumValue注解
    public enum GenderEnum {
    
        MAN(0,"男"),
        WOMAN(1,"女");
    
        @EnumValue
        private Integer gender;
        private String genderName;
    
        GenderEnum(Integer gender, String genderName) {
            this.gender = gender;
            this.genderName = genderName;
        }
    }
    

此时我们再次执行添加操作,发现可以成功添加数据,而枚举类型的值也作为数据被插入到数据库中
在这里插入图片描述

字段类型处理器

  • 在某些场景下,我们在实体类中是使用Map集合作为属性接收前端传递过来的数据的,但是这些数据存储在数据库时,我们使用的是json格式的数据进行存储,json本质是一个字符串,就是varchar类型

  • 那怎么做到实体类的Map类型和数据库的varchar类型的互相转换?

    • 这里就需要使用到字段类型处理器来完成
  • 在实体中是一个map,但是需要转为json的形式存储的,也就是string类型

    • 此时我们实体类的数据和我们数据库的数据是不对应的通过 @TableField注解指定一个字符串处理器,将我们的map类型变成json格式的字符串
    • 在我们实体类上,通过 @TableName(autpResultMap = true) 将我们从数据库中查出来的json数据封装成map
    • 他俩是组合使用的

1️⃣ 我们先在实体类中添加一个字段,Map 类型

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User extends Model<User> {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private GenderEnum gender;
    private Integer status;
    private Map<String,String> contact;//联系方式
}

2️⃣ 在数据库中我们添加一个字段,为varchar类型

在这里插入图片描述

3️⃣ 为实体类添加上对应的注解,实现使用字段类型处理器进行不同类型数据转换

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(autoResultMap = true)//查询时将json字符串封装为Map集合
public class User extends Model<User> {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private GenderEnum gender;
    private Integer status;
    @TableField(typeHandler = FastjsonTypeHandler.class)//指定字段类型处理器
    private Map<String,String> contact;//联系方式
}

4️⃣ 字段类型处理器依赖Fastjson这个Json处理器,所以我们需要引入对应的依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

5️⃣ 测试添加操作

@Test
void typeHandler(){
    User user = new User();
    user.setName("zhang");
    user.setAge(28);
    user.setEmail("zhang@powernode.com");
    user.setGender(GenderEnum.MAN);
    user.setStatus(1);
    HashMap<String, String> contact = new HashMap<>();
    contact.put("phone","010-1234567");
    contact.put("tel","13388889999");
    user.setContact(contact);

    userMapper.insert(user);
}

请添加图片描述

6️⃣ 测试查询操作,通过结果发现,从数据库中查询出来的数据,已经被转到Map集合

@Test
    void typeHandlerSelect(){
        User user = userMapper.selectById(1646422830251880449L);
        System.out.println(user);
    }

请添加图片描述

自动填充功能

在项目中有一些属性,如果我们不希望每次都填充的话,我们可以设置为自动填充,比如常见的时间,创建时间和更新时间可以设置为自动填充

1️⃣ 在数据库的表中添加两个字段

在这里插入图片描述

注意只有设置了下划线和小驼峰映射,这种mysql的写法才能和实体类完成映射
在这里插入图片描述

2️⃣ 在实体类中,添加对应字段,并为需要自动填充的属性指定填充时机

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(autoResultMap = true)
public class User extends Model<User> {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private Integer status;
    private GenderEnum gender;
    @TableField(typeHandler = FastjsonTypeHandler.class)
    private Map<String,String> contact;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}

3️⃣ 编写自动填充处理器,指定填充策略

package com.powernode.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author Bonbons
 * @version 1.0
 */
@Component
public class MyMetaHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        setFieldValByName("createTime", new Date(), metaObject);
        setFieldValByName("updateTime", new Date(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        setFieldValByName("updateTime", new Date(), metaObject);
    }
}

4️⃣ 这里在插入前先设置一下mysql时区,并查看目前时区时间正常
在这里插入图片描述

5️⃣ 再将配置文件的时区修改为 serverTimezone=Asia/Shanghai

在这里插入图片描述

6️⃣ 测试插入操作

@Test
    void testFillInsert(){
        User user = new User();
        user.setName("华佗");
        user.setAge(22);
        user.setEmail("hua@qq.com");
        user.setStatus(1);
        user.setGender(GenderEnum.MAN);
        Map<String, String> map = new HashMap<>();
        map.put("tel", "12366668888");
        map.put("phone", "010-1234567");
        user.setContact(map);

        userMapper.insert(user);
    }

请添加图片描述

7️⃣ 测试更新操作

@Test
    void testFillUpdate(){
        User user = new User();
        user.setId(1646430159940669442L);
        user.setAge(88);
        userMapper.updateById(user);
    }

请添加图片描述
请添加图片描述

防全表更新与删除插件

  • 在实际开发中,全表更新和删除是非常危险的操作,在MybatisPlus中,提供了插件和防止这种危险操作的发生

先演示一下全表更新的场景

@Test
public void testUpdateAll(){
    User user = new User();
    user.setGender(GenderEnum.MAN);
    userService.saveOrUpdate(user,null);
}

在这里插入图片描述

  • 如何解决呢?
    • 注入MybatisPlusInterceptor类,并配置BlockAttackInnerInterceptor拦截器
package com.powernode.config;

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

/**
 * @author Bonbons
 * @version 1.0
 */
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //分页拦截器
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //全表更新拦截器
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        //实现乐观锁控制的拦截器
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

测试全表更新,会出现抛出异常,防止了全表更新

package com.powernode;

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

/**
 * @author Bonbons
 * @version 1.0
 */
@SpringBootTest
public class UpdateAllTest {
    @Autowired
    private UserService userService;

    @Test
    void updateAll(){
        User user = new User();
        user.setId(999L);
        user.setName("wang");
        user.setEmail("wang@powernode.com");
        userService.saveOrUpdate(user,null);
    }
}

请添加图片描述

MybatisX快速开发插件

  • MybatisX是一款IDEA提供的插件,目的是为了我们简化Mybatis以及MybatisPlus框架而生
    我们来看一下,如何在IDEA中安装插件

1️⃣ 首先选择File -> Settings, 选择Plugins,搜索MybatisX,点击安装,这种效果就是安装完毕了

请添加图片描述

2️⃣ 插件安装好以后,我们来看一下插件的功能

(1)Mapper接口和映射文件的跳转功能
在这里插入图片描述

(2)逆向工程

  • 逆向工程就是通过数据库表结构,逆向产生Java工程的结构
    • 包括以下几点:
      (1)实体类
      (2)Mapper接口
      (3)Mapper映射文件
      (4)Service接口
      (5)Service实现类
  • 在这里我们创建一个模块,用于测试逆向工程功能

引入依赖,和编写对应的配置文件信息

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.10</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.powernode</groupId>
	<artifactId>mp03</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>mp03</name>
	<description>mp03</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.31</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.2.8</version>
		</dependency>

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.3</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>p6spy</groupId>
			<artifactId>p6spy</artifactId>
			<version>3.9.1</version>
		</dependency>

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

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

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

1、首先使用IDEA连接mysql,填写连接信息,测试连接通过请添加图片描述

2、找到表右键,选择插件的逆向工程选项
在这里插入图片描述

3、编写逆向工程配置信息
在这里插入图片描述

4、编写生成信息
请添加图片描述
5、观察生成结构,发现下面这些内容都已产生
(1)实体类
(2)Mapper接口
(3)Mapper映射文件
(4)Service接口
(5)Service映射文件
6、接下来我们在Mapper接口上添加@Mapper注解

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

测试代码环境

请添加图片描述

(3)常见需求代码生成

请添加图片描述

  • 虽然Mapper接口中提供了一些常见方法,我们可以直接使用这些常见的方法来完成sql操作
  • 但是对于实际场景中各种复杂的操作需求来说,依然是不够用的
  • 所以MybatisX提供了更多的方法,以及可以根据这些方法直接生成对应的sql语句,这样使得开发变得更加的简单,可以根据名称联想常见的操作
@Mapper
public interface UserMapper extends BaseMapper<User> {
         //添加操作
        int insertSelective(User user);

        //删除操作
        int deleteByNameAndAge(@Param("name") String name, @Param("age") Integer age);

        //修改操作
        int updateNameByAge(@Param("name") String name, @Param("age") Integer age);

        //查询操作
        List<User> selectAllByAgeBetween(@Param("beginAge") Integer beginAge, @Param("endAge") Integer endAge);
}

在映射配置文件中,会生成对应的sql,并不需要我们编写

<insert id="insertSelective">
    insert into powershop_user
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="id != null">id,</if>
        <if test="name != null">name,</if>
        <if test="age != null">age,</if>
        <if test="email != null">email,</if>
    </trim>
    values
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="id != null">#{id,jdbcType=BIGINT},</if>
        <if test="name != null">#{name,jdbcType=VARCHAR},</if>
        <if test="age != null">#{age,jdbcType=INTEGER},</if>
        <if test="email != null">#{email,jdbcType=VARCHAR},</if>
    </trim>
</insert>

<delete id="deleteByNameAndAge">
    delete
    from powershop_user
    where name = #{name,jdbcType=VARCHAR}
      AND age = #{age,jdbcType=NUMERIC}
</delete>

<update id="updateNameByAge">
    update powershop_user
    set name = #{name,jdbcType=VARCHAR}
    where age = #{age,jdbcType=NUMERIC}
</update>

<select id="selectAllByAgeBetween" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from powershop_user
    where
    age between #{beginAge,jdbcType=INTEGER} and #{endAge,jdbcType=INTEGER}
</select>

乐观锁

首先我们先要了解开发中的一个常见场景,叫做并发请求。并发请求就是在同一时刻有多个请求同时请求服务器资源,如果是获取信息,没什么问题,但是如果是对于信息做修改操作呢,那就会出现问题。

这里举一个具体的例子,比如目前商品的库存只剩余1件了,这个时候有多个用户都想要购买这件商品,都发起了购买商品的请求,那么能让这多个用户都购买到么,肯定是不行的,因为多个用户都买到了这件商品,那么就会出现超卖问题,库存不够是没法发货的。所以在开发中就要解决这种超卖的问题
在这里插入图片描述

抛开超卖这一种场景,诸如此类并发访问的场景非常多,这类场景的核心问题就是,一个请求在执行的过程中,其他请求不能改变数据,如果是一次完整的请求,在该请求的过程中其他请求没有对于这个数据产生修改操作,那么这个请求是能够正常修改数据的。如果该请求在改变数据的过程中,已经有其他请求改变了数据,那该请求就不去改变这条数据了。

在这里插入图片描述

想要解决这类问题,最常见的就是加锁的思想,锁可以用验证在请求的执行过程中,是否有数据发生改变。

  • 常见的数据库锁类型有两种,悲观锁和乐观锁。
    一次完成的修改操作是,先查询数据,然后修改数据。

    • 悲观锁:悲观锁是在查询的时候就锁定数据,在这次请求未完成之前,不会释放锁。等到这次请求完毕以后,再释放锁,释放了锁以后,其他请求才可以对于这条数据完成读写

    这样做的操作能够保证读取到的信息就是当前的信息,保证了信息的正确性,但是并发效率很低,在实际开发中使用悲观锁的场景很少,因为在并发时我们是要保证效率的。

    • 乐观锁:乐观锁是通过表字段完成设计的,他的核心思想是,在读取的时候不加锁,其他请求依然可以读取到这个数据,在修改的时候判断一个数据是否有被修改过,如果有被修改过,那本次请求的修改操作失效。

    具体的通过sql是这样实现的

    Update 表 set 字段 = 新值,version = version + 1 where version = 1
    这样做的操作是不会对于数据读取产生影响,并发的效率较高。但是可能目前看到的数据并不是真实信息数据,是被修改之前的,但是在很多场景下是可以容忍的,并不是产生很大影响,例如很多时候我们看到的是有库存,或者都加入到购物车了,但是点进去以后库存没有了。

接下来我们来看一下乐观锁的使用

1️⃣ 在数据库表中添加一个字段version,表示版本,默认值是1

在这里插入图片描述

生成后的效果
在这里插入图片描述

2️⃣ 找到实体类,添加对应的属性,并使用**@Version**标注为这是一个乐观锁字段信息

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    @Version
    private Integer version;
}

3️⃣ 因为要对每条修改语句完成语句的增强,这里我们通过拦截器的配置,让每条修改的sql语句在执行的时候,都加上版本控制的功能

@Configuration
public class MybatisPlusConfig {

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

4️⃣ 测试效果,这里我们模拟先查询,再修改

@Test
void updateTest(){
    User user = userMapper.selectById(6L);
    user.setName("li");
    userMapper.updateById(user);
}

我们通过查看拼接好的SQL语句发现,查询时将User的数据查询出来,是包含version版本信息的

在这里插入图片描述

当我们完成修改时,他会将版本号 + 1

在这里插入图片描述

此时查看数据发现,更改姓名后,version已经为2了
在这里插入图片描述

接下来我们模拟一下,当出现多个修改请求的时候,是否能够做到乐观锁的效果。
乐观锁的效果是,一个请求在修改的过程中,是允许另一个请求查询的,但是修改时会通过版本号是否改变来决定是否修改,如果版本号变了,证明已经有请求修改过数据了,那这次修改不生效,如果版本号没有发生变化,那就完成修改。

@Test
void updateTest2(){
    //模拟操作1的查询操作
    User user1 = userMapper.selectById(6L);

    //模拟操作2的查询操作
    User user2 = userMapper.selectById(6L);

    //模拟操作2的修改操作
    user2.setName("lisi");
    userMapper.updateById(user2);

    //模拟操作1的修改操作
    user1.setName("zhangsan");
    userMapper.updateById(user1);
}

我们来看下这段代码的执行过程,这段代码其实是两次操作,只不过操作1在执行的过程中,有操作2完成了对于数据的修改,这时操作1就无法再次进行修改了
操作1的查询:此时版本为2
在这里插入图片描述

操作2的查询:此时版本为2
在这里插入图片描述

操作2的修改:此时检查版本,版本没有变化,所以完成修改,并将版本改为3
在这里插入图片描述

操作1的修改:此时检查版本,版本已经有最初获取的版本信息发生了变化,所以杜绝修改
在这里插入图片描述

代码生成器

  • 代码生成器和逆向工程的区别在于,代码生成器可以生成更多的结构,更多的内容,允许我们能够配置生成的选项更多。在这里我们演示一下代码生成器的用法
  • 代码生成器不依赖于IDEA和IDEA插件、可以生成到任意目录下、不需要安装IDEA插件、可以生成更多的结构、配置灵活

1️⃣ 参考官网,使用代码生成器需要引入两个依赖

<!--代码生成器依赖-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.3</version>
</dependency>

<!--freemarker模板依赖-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

2️⃣ 编写代码生成器代码

package com.powernode;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Collections;

/**
 * @author Bonbons
 * @version 1.0
 */
@SpringBootTest
public class GenerationApplicationTest {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false", "root", "111111")
                .globalConfig(builder -> {
                    builder.author("powernode") // 设置作者
                            //.enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("D://"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.powernode") // 设置父包名
                            .moduleName("mybatisplus") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
//                    builder.addInclude("powershop_user") // 设置需要生成的表名
//                            .addTablePrefix("powershop"); // 设置过滤表前缀
                    builder.addInclude("user");
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }
}

4️⃣ 执行,查看生成效果请添加图片描述

执行SQL分析打印

  • 在我们日常开发工作当中,避免不了查看当前程序所执行的SQL语句,以及了解它的执行时间,方便分析是否出现了慢SQL问题
  • 我们可以使用MybatisPlus提供的SQL分析打印的功能,来获取SQL语句执行的时间。
    • 就是SpringBoot的执行日志中可以看到SQL的执行时间

1️⃣ 由于该功能依赖于p6spy组件,所以需要在pom.xml中先引入该组件

<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.9.1</version>
</dependency>

2️⃣ 在application.yml中进行配置

将驱动和url修改
spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql

3️⃣ 在resources下,创建 spy.properties配置文件

#3.2.1以上使用modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger

#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger

# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger

# 设置 p6spy driver 代理
deregisterdrivers=true

# 取消JDBC URL前缀
useprefix=true

# 配置记录 Log 例外,可去掉的结果集error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset

# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss

# 实际驱动可多个
#driverlist=org.h2.Driver

# 是否开启慢SQL记录
outagedetection=true

# 慢SQL记录标准 2 秒
outagedetectioninterval=2

4️⃣ 测试
执行查询所有的操作,可以看到sql语句的执行时间
请添加图片描述

多数据源

在学习多数据源之前,我们先来了解一下分库分表。当一个项目的数据库的数据十分庞大时,在完成SQL操作的时候,需要检索的数据就会更多,我们会遇到性能问题,会出现SQL执行效率低的问题。

针对这个问题,我们的解决方案是,将一个数据库中的数据,拆分到多个数据库中,从而减少单个数据库的数据量,从分摊访问请求的压力和减少单个数据库数据量这两个方面,都提升了效率。

我们来演示一下,在MybatisPlus中,如何演示数据源切换的效果

1️⃣ 先创建一个新的模块,将之前模块中的内容复制过来,结构如下
在这里插入图片描述

2️⃣ 引入依赖

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

3️⃣ 创建新的数据库,提供多数据源环境

数据库

在这里插入图片描述

表数据
在这里插入图片描述

4️⃣ 编写配置文件,指定多数据源信息

spring:
  datasource:
    dynamic:
      primary: master
      strict: false
      datasource:
        master:
          username: root
          password: 111111
          url: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          username: root
          password: 111111
          url: jdbc:mysql://localhost:3306/mybatisplus2?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
          driver-class-name: com.mysql.cj.jdbc.Driver

5️⃣ 创建多个Service,分别使用@DS注解描述不同的数据源信息

@Service
@DS("master")
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
}

@Service
@DS("slave_1")
public class UserServiceImpl2 extends ServiceImpl<UserMapper, User> implements UserService{
}

6️⃣ 测试service多数据源环境执行结果

package com.powernode;

import com.powernode.domain.User;
import com.powernode.service.impl.UserServiceImpl;
import com.powernode.service.impl.UserServiceImpl2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @author Bonbons
 * @version 1.0
 */
@SpringBootTest
public class ManyDBTest {
    @Autowired
    private UserServiceImpl userService1;
    @Autowired
    private UserServiceImpl2 userService2;

    @Test
    void test1(){
        User byId = userService1.getById(3L);
        System.out.println(byId);
    }

    @Test
    void test2(){
        User byId = userService2.getById(1L);
        System.out.println(byId);
    }
}

7️⃣ 观察测试结果,发现结果可以从两个数据源中获取

请添加图片描述
在这里插入图片描述

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

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

相关文章

从零学习SDK(5)SDK文档的学习和参考

要想充分利用SDK的优势&#xff0c;仅仅下载和安装SDK是不够的&#xff0c;还需要学习和参考SDK提供的文档和资源。文档和资源是SDK的重要组成部分&#xff0c;它们可以帮助开发者掌握SDK的基本概念、结构、用法、限制和最佳实践&#xff0c;以及解决常见的问题和错误。 查找…

(数字图像处理MATLAB+Python)第三章图像基本运算-第二节:图像代数运算

文章目录一&#xff1a;图像算数运算&#xff08;1&#xff09;加法运算A&#xff1a;概述B&#xff1a;程序&#xff08;2&#xff09;减法运算A&#xff1a;概述B&#xff1a;程序&#xff08;3&#xff09;乘法运算A&#xff1a;概述B&#xff1a;程序&#xff08;4&#xf…

C++模板基础(九)

完美转发与 lambda 表达式模板 void f(int& input) {std::cout << "void f(int& input)\t" << input << \n; }void f(int&& input) {std::cout << "void f(int&& input)\t" << input << \n;…

MYSQL8窗口函数

MYSQL8窗口函数MYSQL8窗口函数窗口函数分类序号函数--排行榜row_number()示例rank()示例dense_rank()示例partition by对每个分区内的行进行排名不加partition by全局排序开窗聚合函数分布函数CUME_DIST()PERCENT_RANK()前后函数LAG()的用法LEAD()头尾函数其他函数NTH_VALUE()N…

用Abp实现两步验证(Two-Factor Authentication,2FA)登录(二):Vue网页端开发

文章目录发送验证码登录退出登录界面控件获取用户信息功能项目地址前端代码的框架采用vue.js elementUI 这套较为简单的方式实现&#xff0c;以及typescript语法更方便阅读。首先添加全局对象&#xff1a; loginForm: 登录表单对象 twoFactorData: 两步验证数据&#xff0c; …

跨平台应用开发进阶(五十九):uni-app实现视屏播放小窗功能

文章目录一、前言二、解决方案三、拓展阅读一、前言 在业务功能开发过程中&#xff0c;需要实现视频直播、播放小窗功能。鉴于目前通过接入火山webSDK实现视频直播、点播功能。需要火山协助配合改造实现小窗功能。 uni-app插件市场也提供了若干插件&#xff0c;经试用效果并不…

从二叉树的角度看快速排序

快速排序本质上可以看作二叉树的前序遍历 快速排序是先将一个元素排好序&#xff0c;然后再将剩下的元素排好序 核心思路依然是分治 快排整体思路 准确的可以说是治分 > 先治 得到分界点后 再分 治&#xff1a;双指针技巧&#xff08;左右指针或者快慢指针&#xff0c;…

【Docker】11、IDEA集成Docker插件实现一键部署SpringBoot项目

日常开发项目的过程中&#xff0c;我们每次需要部署线上的时候&#xff0c;都需要安装一大堆的运行环境&#xff0c;例如&#xff1a;JDK、MySQL、Redis 等&#xff0c;非常花费时间、我们可以使用 Docker 的容器技术&#xff0c;方便快捷地搭建项目启动所需要的运行环境&#…

【微服务笔记15】微服务组件之Config配置中心实现用户认证、配置属性加解密

这篇文章&#xff0c;主要介绍微服务组件之Config配置中心实现用户认证、配置属性加解密。 目录 一、用户认证 1.1、引入security依赖 1.2、服务端ConfigServer添加访问配置 1.3、客户端ConfigClient添加访问配置 二、配置属性加解密 2.1、对称加密 &#xff08;1&#…

逍遥自在学C语言 | 位运算符^的高级用法

前言 在上一篇文章中&#xff0c;我们介绍了|运算符的高级用法&#xff0c;本篇文章&#xff0c;我们将介绍^ 运算符的一些高级用法。 一、人物简介 第一位闪亮登场&#xff0c;有请今后会一直教我们C语言的老师 —— 自在。 第二位上场的是和我们一起学习的小白程序猿 —— …

windows系统管理_文件系统授权规则

NTFS 权限规则 NFTS 权限有一些隐含规则&#xff0c;用户最终有效权限受这些规则的影响&#xff0c;了解并运用这些规则&#xff0c;才能 灵活的分配权限&#xff0c;符合实际需求。 1 权限的累加 如果在某个文件或文件夹的访问控制列表中为某个用户分配了操作权限&#xff…

猿辅导学员入选国家队,竞赛老师成为“最强辅助”

3月31日&#xff0c;国际数学奥林匹克竞赛&#xff08;IMO&#xff09;国家队名单正式出炉&#xff0c;猿辅导学员王淳稷、孙启傲分别以第一名和第二名的成绩位列其中&#xff0c;今年7月&#xff0c;他们将出征日本&#xff0c;代表中国参赛&#xff0c;为国争光。 自2020年以…

理解 与 计算 物联网产品的电池使用寿命

本文带你理解电池的容量以及教会你如何计算使用电池的产品的工作时长前言 在物联网领域&#xff0c;在保证产品性能的前提下&#xff0c;产品的功耗是做得越来越低&#xff0c;针对物联网领域的低功耗无线芯片的功耗也是越来越低。 作为研发人员&#xff0c;除了能够设计出满…

巧用千寻位置GNSS软件|点测量采集技巧

点测量是测量中重要的节点&#xff0c;在测量工作的信息处理分析中发挥着重要作用。本期将给各位带来使用千寻位置GNSS软件采集地形点、控制点、快速点、连续点、房角点和倾斜点的操作技巧。地形点地形点的设置如图 5.1-9所 示&#xff0c;每次采集一个点&#xff0c;该点需要满…

Docker通过Nginx容器代理部署Vue项目

一、打包构建dist 在vue.config.js 添加入口等配置&#xff1a; pages: {index: {// 入口entry: src/main.js}}, lintOnSave: false, publicPath: ./ 在package.json文件中编写build构建&#xff1a; 然后运行: npm run build 在项目根目录下就有构建好的dist包&#xff0…

物流路由线路配载前端算法逻辑实现方案

作者&#xff1a;京东物流 柳宏 1.前置知识 1.1 基本概念 1.1.1 配载 配载代表着某条线路是否具有发往某个方向&#xff08;区域、省市县、分拣等&#xff09;的能力&#xff0c;也可以说是网点&#xff08;分拣中心&#xff09;是否具有承载配载所指方向货物的能力。一般网…

Go 构建基础的事件调度器

&#x1f447;我在这儿 当我们需要在一段时间后的特定时间或间隔运行任务时&#xff0c;我们需要使用任务调度系统来运行任务&#xff1a;例如发送电子邮件、推送通知、午夜关闭账户、清空表格等。在本文中&#xff0c;我们将构建一个基本的事件调度程序&#xff0c;使用数据库…

基于springboot和ajax的简单项目 02.一直会出现的页面的上一页,下一页,分页与总页数 (下)

在各种功能中会一直出现页面分页的问题。 对此&#xff0c;可以使用pojo对象&#xff0c;来一直管理页面分页的功能。 01.创建相关的pojo对象。 由于属性是来辅助sql语句的&#xff0c;这个pojo对象。 Setter Getter ToString NoArgsConstructorpublic class PageObject<T&…

MySQL数据库实现主从同步

安装MySQL数据库8.0.32 前言 今天来学习数据库主从同步的原理及过程&#xff0c;数据库主要是用来存储WEB数据&#xff0c;在企业当中是极为重要的&#xff0c;下面一起来看下。 1.1 数据库做主从的目的 MySQL主从复制在中小企业&#xff0c;大型企业中广泛使用&#xff0c…

【ROS2指南-2】入门 turtlesim 和 rqt

目标&#xff1a;安装并使用 turtlesim 包和 rqt 工具为即将到来的教程做准备。 教程级别&#xff1a;初学者 时间&#xff1a; 15分钟 内容 背景 先决条件 任务 1 安装turtlesim 2 启动turtlesim 3 使用turtlesim 4 安装rqt 5 使用 rqt 6 重新映射 7 关闭turtlesim …