MySQL功能测试-之应用工程
- 前言
- pom.xml
- application.yml 文件
- common.vo 包
- ResultVO
- config 包
- properties 包
- DruidConfigProperty
- DruidMonitorProperty
- AutoFillMetaObjectHandler
- DruidConfig
- FluxConfiguration
- MyBatisPlusConfig
- controller 包
- ClientController
- DruidController
- WebFluxController
- entity 包
- ClientEntity
- exception 包
- code 包
- CRUDStatus
- BizException
- GlobalException
- mapper 包
- ClientMapper
- service 包
- impl 包
- ClientService
- DruidApplication
- resource/mapper 包
- ClientMapper.xml
前言
应用工程的建设,主要是为了支撑 MySQL 数据库的一些测试场景,例如:
- 应用实时写入数据库时,进行修改表结构,查看锁的情况。
- 应用实时写入数据库时,数据库增量备份/恢复测试。
- 应用实时写入数据库时,数据库主从切换脚本测试。等等…各种场景。
当前工程是在《SpringBoot【2】集成 MyBatis Plus》基础上,进行代码迭代开发的,MySQL
数据库相关测试功能所涉及应用仅此一个,故此应用后续会不断完善,也会同步更新此文档。
目前完成后的项目截图如下:
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.12.RELEASE</version>
<relativePath />
</parent>
<groupId>com.junjiu.springboot.druid</groupId>
<artifactId>junjiu-springboot-druid</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<mysql.connector.java.version>8.0.28</mysql.connector.java.version>
<mybatis.plus.boot.starter.version>3.3.1</mybatis.plus.boot.starter.version>
<druid.version>1.2.13</druid.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- MySQL驱动依赖. -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.java.version}</version>
</dependency>
<!-- mybatis-plus 依赖
https://baomidou.com/getting-started/
-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version> ${mybatis.plus.boot.starter.version} </version>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 阿里巴巴 fastjson -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.25</version>
</dependency>
</dependencies>
</project>
application.yml 文件
# 端口
server:
port: 5826
spring:
application:
# 应用名称
name: junjiu-springboot-druid
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.88.54:3306/ideadb?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: fid_idea
password: 123456
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
max-evictable-idle-time-millis: 600000
validation-query: SELECT 1 FROM DUAL
# validation-query-timeout: 5000
test-on-borrow: false
test-on-return: false
test-while-idle: true
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#filters: #配置多个英文逗号分隔(统计,sql注入,log4j过滤)
filters: stat,wall
stat-view-servlet:
enabled: true
url-pattern: /druid/*
# 监控页面配置.
jj:
druid:
monitor:
login-username: root
login-password: 123456
reset-enable: false
# mybatis plus 配置
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
mapper-locations: classpath*:/mapper/*Mapper.xml
type-aliases-package: com.junjiu.springboot.druid.entity
common.vo 包
ResultVO
package com.junjiu.springboot.druid.common.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* program: junjiu-springboot-druid
* ClassName: ResultVO
* description: 定义统一响应 Vo
*
* @author: 君九
* @create: 2024-06-15 10:11
* @version: 1.0
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultVO implements Serializable {
private Integer code;
private String message;
private Object data;
/**
* 状态码
*/
enum Status {
SUCCESS(200, "成功"),
SERVER_ERROR(500, "服务器内部错误");
private Integer code;
private String message;
Status(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return this.code;
}
public String getMessage() {
return this.message;
}
}
public static ResultVO success(String message) {
return new ResultVO(Status.SUCCESS.code, message, null);
}
public static ResultVO success(String message, Object data) {
return new ResultVO(Status.SUCCESS.code, message, data);
}
public static ResultVO error(String message) {
return new ResultVO(Status.SERVER_ERROR.code, message, null);
}
public static ResultVO error(String message, Object data) {
return new ResultVO(Status.SERVER_ERROR.code, message, data);
}
public static ResultVO error(Integer code, String message, Object data) {
return new ResultVO(code, message, data);
}
public static ResultVO error(Integer code, String message) {
return new ResultVO(code, message, null);
}
}
config 包
properties 包
DruidConfigProperty
package com.junjiu.springboot.druid.config.properties;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* program: junjiu-springboot-druid
* ClassName: DruidConfigProperty
* description:
*
* @author: 君九
* @create: 2024-05-26 13:19
* @version: 1.0
**/
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "spring.datasource.druid")
public class DruidConfigProperty {
private String url;
private String username;
private String password;
private String driverClassName;
private int initialSize;
private int maxActive;
private int minIdle;
private int maxWait;
private boolean poolPreparedStatements;
private int maxPoolPreparedStatementPerConnectionSize;
private int timeBetweenEvictionRunsMillis;
private int minEvictableIdleTimeMillis;
private int maxEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private String filters;
private String connectionProperties;
}
DruidMonitorProperty
package com.junjiu.springboot.druid.config.properties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* program: junjiu-springboot-druid
* ClassName: DruidMonitorProperty
* description:
*
* @author: 君九
* @create: 2024-05-26 13:27
* @version: 1.0
**/
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "jj.druid.monitor")
public class DruidMonitorProperty {
private String loginUsername;
private String loginPassword;
private String resetEnable;
}
AutoFillMetaObjectHandler
package com.junjiu.springboot.druid.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* program: junjiu-springboot-druid
* ClassName: AutoFillMetaObjectHandler
* description:
*
* @author: 君九
* @create: 2024-06-12 22:52
* @version: 1.0
**/
@Component
public class AutoFillMetaObjectHandler implements MetaObjectHandler {
/**
* 新增数据时,自动填充创建时间+修改时间 为当前时间。
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createdTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updatedTime", LocalDateTime.class, LocalDateTime.now());
}
/**
* 更新数据时,自动填充创建时间+修改时间 为当前时间。
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "updatedTime", LocalDateTime.class, LocalDateTime.now());
}
}
DruidConfig
package com.junjiu.springboot.druid.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.junjiu.springboot.druid.config.properties.DruidConfigProperty;
import com.junjiu.springboot.druid.config.properties.DruidMonitorProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* program: junjiu-springboot-druid
* ClassName: DruidConfig
* description: Druid 配置文件
*
* @author: 君九
* @create: 2024-05-26 13:16
* @version: 1.0
**/
@Configuration
public class DruidConfig {
@Autowired
DruidConfigProperty druidConfigProperty;
@Autowired
DruidMonitorProperty druidMonitorProperty;
/**
* Druid 连接池配置
*/
@Bean
public DruidDataSource dataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(druidConfigProperty.getUrl());
datasource.setUsername(druidConfigProperty.getUsername());
datasource.setPassword(druidConfigProperty.getPassword());
datasource.setDriverClassName(druidConfigProperty.getDriverClassName());
datasource.setInitialSize(druidConfigProperty.getInitialSize());
datasource.setMinIdle(druidConfigProperty.getMinIdle());
datasource.setMaxActive(druidConfigProperty.getMaxActive());
datasource.setMaxWait(druidConfigProperty.getMaxWait());
datasource.setTimeBetweenEvictionRunsMillis(druidConfigProperty.getTimeBetweenEvictionRunsMillis());
datasource.setMinEvictableIdleTimeMillis(druidConfigProperty.getMinEvictableIdleTimeMillis());
datasource.setMaxEvictableIdleTimeMillis(druidConfigProperty.getMaxEvictableIdleTimeMillis());
datasource.setValidationQuery(druidConfigProperty.getValidationQuery());
datasource.setTestWhileIdle(druidConfigProperty.isTestWhileIdle());
datasource.setTestOnBorrow(druidConfigProperty.isTestOnBorrow());
datasource.setTestOnReturn(druidConfigProperty.isTestOnReturn());
datasource.setPoolPreparedStatements(druidConfigProperty.isPoolPreparedStatements());
datasource.setMaxPoolPreparedStatementPerConnectionSize(druidConfigProperty.getMaxPoolPreparedStatementPerConnectionSize());
try {
datasource.setFilters(druidConfigProperty.getFilters());
} catch (Exception ex) {
ex.printStackTrace();
}
datasource.setConnectionProperties(druidConfigProperty.getConnectionProperties());
return datasource;
}
/**
* JDBC操作配置
*/
@Bean
public JdbcTemplate jdbcTemplate (@Autowired DruidDataSource dataSource){
return new JdbcTemplate(dataSource) ;
}
/**
* 配置 Druid 监控界面
*/
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
//设置控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername",druidMonitorProperty.getLoginUsername());
servletRegistrationBean.addInitParameter("loginPassword",druidMonitorProperty.getLoginPassword());
// 是否可以重置数据
servletRegistrationBean.addInitParameter("resetEnable",druidMonitorProperty.getResetEnable());
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean statFilter(){
//创建过滤器
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
//设置过滤器过滤路径
filterRegistrationBean.addUrlPatterns("/*");
//忽略过滤的形式
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
FluxConfiguration
package com.junjiu.springboot.druid.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import reactor.core.publisher.DirectProcessor;
import reactor.core.publisher.FluxSink;
/**
* program: junjiu-springboot-druid
* ClassName: FluxConfiguration
* description:
*
* @author: 君九
* @create: 2024-06-15 12:16
* @version: 1.0
**/
@Configuration
public class FluxConfiguration {
@Bean
public DirectProcessor<String> directProcessor() {
return DirectProcessor.create();
}
@Bean
public FluxSink<String> fluxSink(DirectProcessor<String> directProcessor) {
return directProcessor.sink();
}
}
MyBatisPlusConfig
package com.junjiu.springboot.druid.config;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* program: junjiu-springboot-druid
* ClassName: MyBatisPlusConfig
* description:
*
* @author: 君九
* @create: 2024-06-12 22:49
* @version: 1.0
**/
@Configuration
public class MyBatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* 自动填充功能
* @return
*/
@Bean
public GlobalConfig globalConfig() {
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setMetaObjectHandler(new AutoFillMetaObjectHandler());
return globalConfig;
}
}
controller 包
ClientController
package com.junjiu.springboot.druid.controller;
import com.junjiu.springboot.druid.common.vo.ResultVO;
import com.junjiu.springboot.druid.entity.ClientEntity;
import com.junjiu.springboot.druid.service.ClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.time.Duration;
/**
* program: junjiu-springboot-druid
* ClassName: ClientController
* description:
*
* @author: 君九
* @create: 2024-06-12 22:59
* @version: 1.0
**/
@RestController
@RequestMapping("/client")
public class ClientController {
@Autowired
private ClientService clientService;
/**
* 测试添加数据.
* @return
*/
@RequestMapping("/testAdd")
public String testAdd() {
ClientEntity clientEntity = new ClientEntity();
clientEntity.setClientId(1001L);
clientEntity.setNickName("九皇叔叔");
clientEntity.setRealName("君九");
clientEntity.setUserNo("JunJiu");
clientEntity.setUserPassword("123456");
clientService.save(clientEntity);
return "Success.";
}
/**
* 我当前做测试,传入 loop、threadSize 一定有值,不在校验了
*
* @param loop 是否循环
* @param threadSize 线程个数.
* @return
*/
@GetMapping("/setAddRandomData/{loop}/{threadSize}")
public ResultVO setAddRandomData(
@PathVariable("loop") Boolean loop,
@PathVariable("threadSize") Integer threadSize
) {
clientService.setAddRandomData(loop, threadSize);
return ResultVO.success("操作成功.loop=" + loop);
}
}
DruidController
package com.junjiu.springboot.druid.controller;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* program: junjiu-springboot-druid
* ClassName: DruidController
* description:
*
* @author: 君九
* @create: 2024-05-26 13:32
* @version: 1.0
**/
@RestController
public class DruidController {
@Resource
private JdbcTemplate jdbcTemplate;
@Autowired
private DruidDataSource druidDataSource;
/**
* 测试 Druid
* @return
*/
@GetMapping("/test")
public String testDruid() {
String sql = "select version()";
String result = jdbcTemplate.queryForObject(sql, String.class);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return "Success.".concat(simpleDateFormat.format(new Date())).concat(":").concat(result);
}
/**
* 测试连接池.
* 查看 performance_schema.threads 情况.
* @param size
* @return
*/
@GetMapping("/mutix/{size}")
public String testDruidMutix(@PathVariable("size") Integer size) {
for(int k = 0; k < size; k++) {
new Thread(() -> {
String sql = "select '你也可以通过操作系统级别的资源限制来控制 MySQL 进程的内存使用情况。具体的操作方式会根据操作系统的不同而有所不同,比如在 Linux 中可以使用 ulimit 命令来设置进程的资源限制。'";
String result = jdbcTemplate.queryForObject(sql, String.class);
System.out.println(Thread.currentThread().getName() + ":" + result);
}).start();
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return "Success.".concat(simpleDateFormat.format(new Date()));
}
}
WebFluxController
package com.junjiu.springboot.druid.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.time.Duration;
/**
* program: junjiu-springboot-druid
* ClassName: WebFluxController
* description:
*
* @author: 君九
* @create: 2024-06-15 11:26
* @version: 1.0
**/
@RestController
public class WebFluxController {
@GetMapping("/data")
public Flux<String> streamData() {
// 模拟生成实时数据流
return Flux.just("Data 1", "Data 2", "Data 3", "Data 4").delayElements(Duration.ofSeconds(1));
}
}
entity 包
ClientEntity
package com.junjiu.springboot.druid.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* program: junjiu-springboot-druid
* ClassName: ClientEntity
* description:
*
* @author: 君九
* @create: 2024-06-12 22:55
* @version: 1.0
**/
@Data
@TableName("client")
public class ClientEntity implements Serializable {
/**
* -- 1 -* 客户表
* create table client(
* id bigint not null primary key auto_increment comment '自增ID',
* client_id bigint not null comment 'ID编号',
* user_no varchar(30) comment '账号',
* user_password varchar(60) comment '密码',
* nick_name varchar(30) comment '昵称',
* real_name varchar(30) comment '真实姓名',
* created_time datetime default now() comment '创建时间',
* upated_time datetime default now() comment '更新时间'
* );
*/
private Long id;
private Long clientId;
private String userNo;
private String userPassword;
private String nickName;
private String realName;
private Date createdTime;
private Date updatedTime;
}
exception 包
code 包
CRUDStatus
package com.junjiu.springboot.druid.exception.code;
/**
* program: junjiu-springboot-druid
* ClassName: CRUDStatus
* description: CRUD操作通用状态码.
*
* @author: 君九
* @create: 2024-06-15 10:09
* @version: 1.0
**/
public enum CRUDStatus {
// 添加成功
SUCCESS_INSERT(100001, "添加成功"),
// 添加失败
FAIL_INSERT(100002, "添加失败"),
// 修改成功
SUCCESS_UPDATE(100003, "修改成功"),
// 修改失败
FAIL_UPDATE(100004, "修改失败"),
// 删除成功
SUCCESS_DELETE(100005, "删除成功"),
// 删除失败
FAIL_DELETE(100006, "删除失败"),
// 未查询到数据
NOT_FOUND(100007, "未查询到数据"),
// 非法数据信息
ILLEGAL_ARGUMENT(100008, "非法数据信息");
private Integer code;
private String message;
CRUDStatus(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return this.code;
}
public String getMessage() {
return this.message;
}
}
BizException
package com.junjiu.springboot.druid.exception;
import lombok.Data;
/**
* program: junjiu-springboot-druid
* ClassName: BizException
* description: 定义业务类统一异常类
*
* @author: 君九
* @create: 2024-06-15 10:08
* @version: 1.0
**/
@Data
public class BizException extends RuntimeException {
private Integer code;
private String message;
public BizException(String message) {
super(message);
this.message = message;
}
public BizException(Integer code, String message) {
this.code = code;
this.message = message;
}
public BizException(String message, Exception exception) {
super(message, exception);
this.message = message;
}
public BizException(Integer code, String message, Exception exception) {
super(message, exception);
this.code = code;
this.message = message;
}
}
GlobalException
package com.junjiu.springboot.druid.exception;
/**
* program: junjiu-springboot-druid
* ClassName: GlobalException
* description:
*
* @author: 君九
* @create: 2024-06-15 10:08
* @version: 1.0
**/
public class GlobalException {
}
mapper 包
ClientMapper
package com.junjiu.springboot.druid.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.junjiu.springboot.druid.entity.ClientEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* program: junjiu-springboot-druid
* ClassName: ClientMapper.xml
* description:
*
* @author: 君九
* @create: 2024-06-12 22:57
* @version: 1.0
**/
@Mapper
public interface ClientMapper extends BaseMapper<ClientEntity> {
}
service 包
impl 包
package com.junjiu.springboot.druid.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.junjiu.springboot.druid.entity.ClientEntity;
import com.junjiu.springboot.druid.exception.BizException;
import com.junjiu.springboot.druid.exception.code.CRUDStatus;
import com.junjiu.springboot.druid.mapper.ClientMapper;
import com.junjiu.springboot.druid.service.ClientService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* program: junjiu-springboot-druid
* ClassName: ClientServiceImpl
* description:
* 客户业务实现类.
*
* @author: 君九
* @create: 2024-06-12 22:58
* @version: 1.0
**/
@Service
public class ClientServiceImpl extends ServiceImpl<ClientMapper, ClientEntity> implements ClientService {
private ThreadLocal<Thread> threadLocal = new ThreadLocal<>(); // ThreadLocal 存储每个线程
private volatile boolean loop = true; // 用于控制线程循环的共享变量
@Override
@Transactional(rollbackFor = Exception.class)
public void setAddRandomData(Boolean loop, Integer threadSize) {
this.loop = loop; // 设置共享变量的初始值
CompletableFuture<?>[] futures = new CompletableFuture[threadSize];
if(loop) {
for (int i = 0; i < threadSize; i++) {
final int threadNumber = i + 1;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
while (this.loop) {
// 创建随机的 ClientEntity 对象
ClientEntity client = new ClientEntity();
client.setClientId(Thread.currentThread().getId()); // 设置随机的 clientId
client.setUserNo(Thread.currentThread().getName()); // 将 userNo 设置为当前线程名称
client.setUserPassword(generateRandomString(16)); // 生成随机密码
client.setNickName("NickName_" + Thread.currentThread().getId()); // 设置昵称
client.setRealName("RealName_" + Thread.currentThread().getId()); // 设置真实姓名
try {
// 将生成的 ClientEntity 插入数据库
baseMapper.insert(client);
} catch (Exception exception) {
exception.printStackTrace();
throw new BizException(CRUDStatus.FAIL_DELETE.getCode(), CRUDStatus.FAIL_INSERT.getMessage(), exception);
}
Random random = new Random();
int randomData = random.nextInt(1000);
try {
Thread.sleep(randomData);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
futures[i] = future.exceptionally(ex -> {
// 异常处理
if (ex.getCause() instanceof BizException) {
throw (BizException) ex.getCause(); // 将 BizException 抛给父线程
} else {
// 其他异常,可以选择处理或包装成自定义异常
throw new RuntimeException("Unexpected error occurred in thread " + threadNumber, ex);
}
});
threadLocal.set(Thread.currentThread());
}
try {
CompletableFuture.allOf(futures).get(); // 等待所有 CompletableFuture 完成
} catch (InterruptedException | ExecutionException e) {
// 处理异常
// e.printStackTrace();
System.out.println(e.getMessage());
}
} else {
this.loop = false; // 设置 loop 为 false,停止所有线程的循环
}
}
/**
* 生成指定长度的随机字符串(仅用于示例,实际应用中需要更复杂的实现)
* @param length 字符串长度
* @return 随机字符串
*/
private String generateRandomString(int length) {
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < length; i++) {
int index = random.nextInt(characters.length());
sb.append(characters.charAt(index));
}
return sb.toString();
}
}
ClientService
package com.junjiu.springboot.druid.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.junjiu.springboot.druid.entity.ClientEntity;
/**
* program: junjiu-springboot-druid
* ClassName: ClientService
* description:
* 客户端业务接口.
*
* @author: 君九
* @create: 2024-06-12 22:57
* @version: 1.0
**/
public interface ClientService extends IService<ClientEntity> {
/**
* 随机添加数据,用于模拟:应用实时添加数据情况,对 MySQL 数据库进行备份恢复、调整字段长度等场景。
*
* --- 目标:
* 1.根据传入的线程个数,开启多线程。
* 2.产生随机等待时长,使线程等待后,在执行添加数据操作。
* 3.可手动开启/暂停,数据入库。
*
* @param loop 循环.
* @param threadSize 线程个数.
*
*/
void setAddRandomData(Boolean loop, Integer threadSize);
}
DruidApplication
package com.junjiu.springboot.druid;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* program: junjiu-springboot-druid
* ClassName: DruidApplication
* description:
*
* @author: 君九
* @create: 2024-05-26 13:13
* @version: 1.0
**/
@SpringBootApplication
public class DruidApplication {
public static void main(String[] args) {
SpringApplication.run(DruidApplication.class);
}
}
resource/mapper 包
ClientMapper.xml
<?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.junjiu.springboot.druid.mapper.ClientMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.junjiu.springboot.druid.entity.ClientEntity">
<id column="id" property="id" />
<result column="client_id" property="clientId" />
<result column="user_no" property="userNo" />
<result column="user_password" property="userPassword" />
<result column="nick_name" property="nickName" />
<result column="real_name" property="realName" />
<result column="created_time" property="createdTime" />
<result column="updated_time" property="updatedTime" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, client_id, user_no, user_password, nick_name, real_name, created_time, updated_time
</sql>
</mapper>