概述
MyBatis-Plus 是 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,提供了更强大的 CRUD 操作和代码生成功能。Druid 是阿里巴巴开源的数据库连接池,具有优秀的监控和扩展功能。
技术栈介绍
- MyBatis-Plus: MyBatis 增强工具,提供 CRUD 接口和代码生成器
- Druid: 高性能数据库连接池,具备监控和防火墙功能
- SpringBoot: 提供自动配置和快速集成
核心优势
| 技术 | 主要优势 | 应用场景 |
|---|---|---|
| MyBatis-Plus | 零配置、强大的 CRUD、代码生成器 | 简化数据库操作,提高开发效率 |
| Druid | 监控统计、SQL 防火墙、高性能 | 生产环境数据库连接管理 |
💡 提示: MyBatis-Plus 和 Druid 的结合可以极大提升开发效率和应用性能。
环境准备
系统要求
- JDK 8+
- Maven 3.6+ 或 Gradle 6+
- MySQL 5.7+ 或其他关系型数据库
- SpringBoot 2.0+
Maven 依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-mybatis-plus-druid</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
<mybatis-plus.version>3.5.4</mybatis-plus.version>
<druid.version>1.2.20</druid.version>
</properties>
<dependencies>
<!-- SpringBoot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus SpringBoot Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- Druid SpringBoot Starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SpringBoot 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 代码生成器依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.4</version>
<scope>test</scope>
</dependency>
<!-- 模板引擎(代码生成器需要) -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
整合 MyBatis-Plus
1. 基础配置
application.yml 配置
# 服务器配置
server:
port: 8080
# Spring 配置
spring:
application:
name: springboot-mybatis-plus-druid
# MyBatis-Plus 配置
mybatis-plus:
# 配置扫描通用枚举,支持统计等操作
type-enums-package: com.example.enums
# 全局配置
global-config:
# 数据库配置
db-config:
# 设置所有表的通用前缀名称
table-prefix: tbl_
# 主键策略(AUTO: 数据库自增,ASSIGN_ID: 雪花算法)
id-type: ASSIGN_ID
# 逻辑删除字段名
logic-delete-field: deleted
# 逻辑删除值(删除)
logic-delete-value: 1
# 逻辑删除值(未删除)
logic-not-delete-value: 0
# 字段验证策略
insert-strategy: NOT_NULL
update-strategy: NOT_NULL
select-strategy: NOT_NULL
# 配置文件
configuration:
# 开启驼峰命名转换
map-underscore-to-camel-case: true
# 开启缓存
cache-enabled: true
# 开启延迟加载
lazy-loading-enabled: true
# 开启二级缓存
local-cache-scope: STATEMENT
# 日志实现
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# Mapper XML 文件位置
mapper-locations: classpath*:/mapper/**/*.xml
# 实体类别名包路径
type-aliases-package: com.example.entity
# 分页插件配置
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql
# 日志配置
logging:
level:
com.example.mapper: debug
com.alibaba.druid: info
pattern:
console: '%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n'
2. 实体类定义
使用 MyBatis-Plus 注解来简化配置:
package com.example.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tbl_book")
public class Book {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 书籍类型
*/
@TableField("type")
private String type;
/**
* 书籍名称
*/
@TableField("name")
private String name;
/**
* 书籍描述
*/
@TableField("description")
private String description;
/**
* 作者
*/
@TableField("author")
private String author;
/**
* 价格
*/
@TableField("price")
private Double price;
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 逻辑删除标识
*/
@TableLogic
@TableField("deleted")
private Integer deleted;
/**
* 版本号(乐观锁)
*/
@Version
@TableField("version")
private Integer version;
}
3. Mapper 接口定义
继承 BaseMapper 获得基础 CRUD 功能:
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.entity.Book;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface BookMapper extends BaseMapper<Book> {
/**
* 根据类型查询书籍列表
*/
@Select("SELECT * FROM tbl_book WHERE type = #{type} AND deleted = 0")
List<Book> findByType(@Param("type") String type);
/**
* 分页查询书籍
*/
@Select("SELECT * FROM tbl_book WHERE deleted = 0 ORDER BY create_time DESC")
IPage<Book> selectBookPage(Page<Book> page);
/**
* 根据价格区间查询
*/
@Select("SELECT * FROM tbl_book WHERE price BETWEEN #{minPrice} AND #{maxPrice} AND deleted = 0")
List<Book> findByPriceRange(@Param("minPrice") Double minPrice, @Param("maxPrice") Double maxPrice);
/**
* 统计各类型书籍数量
*/
@Select("SELECT type, COUNT(*) as count FROM tbl_book WHERE deleted = 0 GROUP BY type")
List<Object> countByType();
/**
* 自定义复杂查询
*/
List<Book> findByCustomCondition(@Param("type") String type,
@Param("author") String author,
@Param("minPrice") Double minPrice,
@Param("maxPrice") Double maxPrice);
}
4. Service 层实现
使用 MyBatis-Plus 提供的 IService 接口:
package com.example.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.entity.Book;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
public interface BookService extends IService<Book> {
/**
* 根据类型查询书籍
*/
List<Book> findByType(String type);
/**
* 分页查询书籍
*/
IPage<Book> selectBookPage(Page<Book> page);
/**
* 根据价格区间查询
*/
List<Book> findByPriceRange(Double minPrice, Double maxPrice);
/**
* 批量保存书籍
*/
boolean saveBatchBooks(List<Book> books);
/**
* 根据ID逻辑删除
*/
boolean logicDeleteById(Long id);
}
Service 实现类:
package com.example.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.Book;
import com.example.mapper.BookMapper;
import com.example.service.BookService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {
@Override
public List<Book> findByType(String type) {
return baseMapper.findByType(type);
}
@Override
public IPage<Book> selectBookPage(Page<Book> page) {
return baseMapper.selectBookPage(page);
}
@Override
public List<Book> findByPriceRange(Double minPrice, Double maxPrice) {
return baseMapper.findByPriceRange(minPrice, maxPrice);
}
@Override
public boolean saveBatchBooks(List<Book> books) {
return this.saveBatch(books);
}
@Override
public boolean logicDeleteById(Long id) {
return this.removeById(id);
}
/**
* 使用 QueryWrapper 进行条件查询
*/
public List<Book> findByCondition(String type, String author, Double minPrice) {
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(type != null, "type", type)
.like(author != null, "author", author)
.ge(minPrice != null, "price", minPrice)
.orderByDesc("create_time");
return this.list(queryWrapper);
}
}
整合 Druid 连接池
1. Druid 配置
application.yml 中的 Druid 配置
spring:
datasource:
# Druid 数据源配置
druid:
# 数据库连接信息
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&allowPublicKeyRetrieval=true
username: root
password: password123
# 连接池配置
# 初始化连接数
initial-size: 5
# 最小连接数
min-idle: 5
# 最大连接数
max-active: 20
# 获取连接时最大等待时间,单位毫秒
max-wait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位毫秒
time-between-eviction-runs-millis: 60000
# 配置一个连接在池中最小生存的时间,单位毫秒
min-evictable-idle-time-millis: 300000
# 配置一个连接在池中最大生存的时间,单位毫秒
max-evictable-idle-time-millis: 900000
# 验证连接有效性
validation-query: SELECT 1 FROM DUAL
# 申请连接时执行validationQuery检测连接是否有效
test-while-idle: true
# 申请连接时执行validationQuery检测连接是否有效
test-on-borrow: false
# 归还连接时执行validationQuery检测连接是否有效
test-on-return: false
# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计
# stat: 监控统计
# log4j: 日志
# wall: 防火墙(防止SQL注入)
filters: stat,wall,log4j2
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 配置监控服务器
stat-view-servlet:
enabled: true
# 访问路径
url-pattern: /druid/*
# IP白名单(没有配置或者为空,则允许所有访问)
allow: 127.0.0.1,192.168.1.0/24
# IP黑名单(存在共同时,deny优先于allow)
deny: 192.168.1.73
# 禁用HTML页面上的"Reset All"功能
reset-enable: false
# 登录名
login-username: admin
# 登录密码
login-password: 123456
# 配置Web应用监控
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# 配置Spring监控
aop-patterns: com.example.service.*
2. Druid 配置类
创建自定义配置类来进一步定制 Druid:
package com.example.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfig {
/**
* 配置Druid数据源
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource druidDataSource() {
return new DruidDataSource();
}
/**
* 配置Druid监控页面
*/
@Bean
public ServletRegistrationBean<StatViewServlet> statViewServlet() {
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(
new StatViewServlet(), "/druid/*"
);
Map<String, String> initParams = new HashMap<>();
// 访问用户名和密码
initParams.put("loginUsername", "admin");
initParams.put("loginPassword", "123456");
// 默认允许所有访问
initParams.put("allow", "");
// 拒绝访问的IP
initParams.put("deny", "192.168.1.73");
bean.setInitParameters(initParams);
return bean;
}
/**
* 配置web监控filter
*/
@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilter() {
FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new WebStatFilter());
Map<String, String> initParams = new HashMap<>();
// 不拦截的请求
initParams.put("exclusions", "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
bean.setInitParameters(initParams);
// 拦截所有请求
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
完整集成示例
1. MyBatis-Plus 配置类
package com.example.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
@Configuration
public class MybatisPlusConfig {
/**
* 配置分页插件和乐观锁插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
/**
* 自动填充处理器
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
};
}
}
2. 控制器实现
package com.example.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.entity.Book;
import com.example.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/books")
public class BookController {
@Autowired
private BookService bookService;
/**
* 分页查询书籍
*/
@GetMapping("/page")
public ResponseEntity<IPage<Book>> getBookPage(
@RequestParam(defaultValue = "1") Long current,
@RequestParam(defaultValue = "10") Long size) {
Page<Book> page = new Page<>(current, size);
IPage<Book> bookPage = bookService.selectBookPage(page);
return ResponseEntity.ok(bookPage);
}
/**
* 根据ID查询书籍
*/
@GetMapping("/{id}")
public ResponseEntity<Book> getBook(@PathVariable Long id) {
Book book = bookService.getById(id);
return book != null ? ResponseEntity.ok(book) : ResponseEntity.notFound().build();
}
/**
* 根据类型查询书籍
*/
@GetMapping("/type/{type}")
public List<Book> getBooksByType(@PathVariable String type) {
return bookService.findByType(type);
}
/**
* 根据价格区间查询
*/
@GetMapping("/price")
public List<Book> getBooksByPriceRange(
@RequestParam Double minPrice,
@RequestParam Double maxPrice) {
return bookService.findByPriceRange(minPrice, maxPrice);
}
/**
* 创建书籍
*/
@PostMapping
public ResponseEntity<String> createBook(@RequestBody Book book) {
boolean result = bookService.save(book);
return result ? ResponseEntity.ok("创建成功") : ResponseEntity.badRequest().body("创建失败");
}
/**
* 批量创建书籍
*/
@PostMapping("/batch")
public ResponseEntity<String> createBooks(@RequestBody List<Book> books) {
boolean result = bookService.saveBatchBooks(books);
return result ? ResponseEntity.ok("批量创建成功") : ResponseEntity.badRequest().body("批量创建失败");
}
/**
* 更新书籍
*/
@PutMapping("/{id}")
public ResponseEntity<String> updateBook(@PathVariable Long id, @RequestBody Book book) {
book.setId(id);
boolean result = bookService.updateById(book);
return result ? ResponseEntity.ok("更新成功") : ResponseEntity.badRequest().body("更新失败");
}
/**
* 删除书籍(逻辑删除)
*/
@DeleteMapping("/{id}")
public ResponseEntity<String> deleteBook(@PathVariable Long id) {
boolean result = bookService.logicDeleteById(id);
return result ? ResponseEntity.ok("删除成功") : ResponseEntity.badRequest().body("删除失败");
}
/**
* 获取所有书籍
*/
@GetMapping
public List<Book> getAllBooks() {
return bookService.list();
}
/**
* 统计书籍总数
*/
@GetMapping("/count")
public Long getBookCount() {
return bookService.count();
}
}
高级配置
1. 代码生成器
创建代码生成器来自动生成实体类、Mapper、Service 等:
package com.example.generator;
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.VelocityTemplateEngine;
import java.sql.Types;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/springboot_demo", "root", "password123")
.globalConfig(builder -> {
builder.author("系统管理员") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://"); // 指定输出目录
})
.dataSourceConfig(builder ->
builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
})
)
.packageConfig(builder -> {
builder.parent("com.example") // 设置父包名
.moduleName("demo") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://mapper")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("tbl_book") // 设置需要生成的表名
.addTablePrefix("tbl_") // 设置过滤表前缀
.entityBuilder()
.enableLombok() // 开启 lombok 模型
.enableTableFieldAnnotation() // 开启生成实体时生成字段注解
.controllerBuilder()
.enableRestStyle() // 开启生成@RestController 控制器
.mapperBuilder()
.enableMapperAnnotation() // 开启 @Mapper 注解
.serviceBuilder()
.formatServiceFileName("%sService") // 格式化 service 接口文件名称
.formatServiceImplFileName("%sServiceImpl"); // 格式化 service 实现类文件名称
})
.templateEngine(new VelocityTemplateEngine()) // 使用Velocity引擎模板,默认的是Velocity引擎模板
.execute();
}
}
2. 多数据源配置
@Configuration
public class MybatisPlusMultiDataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("primary", primaryDataSource());
dataSourceMap.put("secondary", secondaryDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
dynamicDataSource.setDefaultTargetDataSource(primaryDataSource());
return dynamicDataSource;
}
}
监控与管理
1. Druid 监控面板
访问监控面板:
- URL: http://localhost:8080/druid/
- 用户名: admin
- 密码: 123456
监控面板提供以下功能:
- 数据源监控: 连接池状态、活跃连接数等
- SQL监控: SQL执行统计、慢SQL分析
- URI监控: Web URI访问统计
- Session监控: 用户Session统计
- JSON API: 监控数据的JSON接口
2. SQL 监控配置
spring:
datasource:
druid:
filter:
stat:
enabled: true
# 慢SQL记录
slow-sql-millis: 1000
log-slow-sql: true
# 合并SQL
merge-sql: true
wall:
enabled: true
config:
# 是否允许执行SELECT语句
select-allow: true
# 是否允许执行DELETE语句
delete-allow: true
# 是否允许执行UPDATE语句
update-allow: true
3. 自定义监控指标
@Component
public class DatabaseMetrics {
@Autowired
private DataSource dataSource;
@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
if (dataSource instanceof DruidDataSource) {
DruidDataSource druidDataSource = (DruidDataSource) dataSource;
// 注册自定义指标
Gauge.builder("datasource.active.connections")
.description("Number of active connections")
.register(Metrics.globalRegistry, druidDataSource, DruidDataSource::getActiveCount);
Gauge.builder("datasource.max.connections")
.description("Maximum number of connections")
.register(Metrics.globalRegistry, druidDataSource, DruidDataSource::getMaxActive);
}
}
}
性能优化
1. 连接池优化
spring:
datasource:
druid:
# 根据应用实际情况调整连接池大小
initial-size: 10
min-idle: 10
max-active: 50
# 连接超时设置
max-wait: 30000
# 连接回收设置
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
# 预编译语句缓存
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 50
2. MyBatis-Plus 优化
@Configuration
public class MybatisPlusOptimizeConfig {
/**
* SQL 性能分析插件(开发环境使用)
*/
@Bean
@Profile({"dev", "test"})
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
// 设置SQL执行最大时长,超过则停止运行
performanceInterceptor.setMaxTime(1000);
// 设置SQL格式化
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
/**
* 数据权限插件
*/
@Bean
public DataPermissionInterceptor dataPermissionInterceptor() {
return new DataPermissionInterceptor();
}
}
测试验证
1. 单元测试
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class BookServiceTest {
@Autowired
private BookService bookService;
@Test
void testSaveAndQuery() {
// 创建测试数据
Book book = new Book()
.setType("技术")
.setName("MyBatis-Plus实战")
.setDescription("MyBatis-Plus开发指南")
.setAuthor("张三")
.setPrice(88.00);
// 保存测试
boolean saveResult = bookService.save(book);
assertTrue(saveResult);
assertNotNull(book.getId());
// 查询测试
Book savedBook = bookService.getById(book.getId());
assertNotNull(savedBook);
assertEquals("技术", savedBook.getType());
// 更新测试
savedBook.setPrice(99.00);
boolean updateResult = bookService.updateById(savedBook);
assertTrue(updateResult);
// 删除测试(逻辑删除)
boolean deleteResult = bookService.removeById(savedBook.getId());
assertTrue(deleteResult);
// 验证逻辑删除
Book deletedBook = bookService.getById(savedBook.getId());
assertNull(deletedBook);
}
@Test
void testPageQuery() {
// 分页查询测试
Page<Book> page = new Page<>(1, 5);
IPage<Book> result = bookService.selectBookPage(page);
assertNotNull(result);
assertTrue(result.getTotal() >= 0);
assertTrue(result.getRecords().size() <= 5);
}
@Test
void testBatchOperation() {
// 批量操作测试
List<Book> books = Arrays.asList(
new Book().setType("文学").setName("红楼梦").setAuthor("曹雪芹").setPrice(56.00),
new Book().setType("文学").setName("西游记").setAuthor("吴承恩").setPrice(48.00),
new Book().setType("文学").setName("水浒传").setAuthor("施耐庵").setPrice(52.00)
);
boolean batchResult = bookService.saveBatch(books);
assertTrue(batchResult);
// 验证批量保存结果
List<Book> savedBooks = bookService.findByType("文学");
assertTrue(savedBooks.size() >= 3);
}
}
2. 集成测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class BookControllerIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void testBookCrud() {
// 创建书籍
Book book = new Book()
.setType("技术")
.setName("Spring Boot实战")
.setAuthor("李四")
.setPrice(78.00);
ResponseEntity<String> createResponse = restTemplate.postForEntity("/api/books", book, String.class);
assertEquals(HttpStatus.OK, createResponse.getStatusCode());
// 查询书籍列表
ResponseEntity<Book[]> listResponse = restTemplate.getForEntity("/api/books", Book[].class);
assertEquals(HttpStatus.OK, listResponse.getStatusCode());
assertTrue(listResponse.getBody().length > 0);
}
}
常见问题
问题1:Druid 监控页面无法访问
现象: 访问 /druid/ 返回 404 原因: Druid 监控未正确配置 解决方案:
spring:
datasource:
druid:
stat-view-servlet:
enabled: true
url-pattern: /druid/*
问题2:MyBatis-Plus 表前缀不生效
现象: SQL 中没有表前缀 原因: 配置路径错误 解决方案:
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
问题3:分页查询不生效
现象: 分页参数不起作用 原因: 未配置分页插件 解决方案:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
问题4:逻辑删除不生效
现象: 删除操作执行物理删除 原因: 逻辑删除字段未正确配置 解决方案:
@TableLogic
@TableField("deleted")
private Integer deleted;
问题5:连接池连接泄露
现象: 连接数持续增长 原因: 连接未正确释放 解决方案:
spring:
datasource:
druid:
# 开启连接泄露检测
remove-abandoned: true
remove-abandoned-timeout: 1800
⚠️ 注意: 在生产环境中,请根据实际情况调整连接池参数,并定期监控数据库连接状态。
相关文章
SpringBoot 系列
数据库相关
Java 基础
- [Java EE 企业级开发](../JAVA EE/)
问题排查
运维部署
监控运维
上一章 / 下一章
- 上一章:2.整合JUnit、Mybatis.md
- 下一章:无