灵活# Spring Boot + MyBatis Plus 最佳实践:从代码生成到数据库优化的完整指南
引言
在现代Java后端开发中,Spring Boot与MyBatis Plus的组合已经成为构建高效、稳定后端服务的主流选择。Spring Boot凭借其自动配置和快速开发特性,极大地提升了开发效率;而MyBatis Plus作为MyBatis的增强工具,提供了丰富的CRUD操作和便捷的代码生成能力。本文将深入探讨这一技术组合的最佳实践,从代码生成到数据库优化的完整流程,帮助开发者构建高质量的后端服务系统。
一、Spring Boot + MyBatis Plus 环境搭建
1.1 项目依赖配置
在开始开发之前,首先需要配置正确的项目依赖。以下是Maven配置示例:
<dependencies>
<!-- Spring Boot Web启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus启动器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
1.2 配置文件设置
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
pool-name: MyHikariCP
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
mapper-locations: classpath*:/mapper/**/*.xml
二、代码自动生成实践
2.1 MyBatis Plus Generator配置
MyBatis Plus提供了强大的代码生成器,可以自动生成Entity、Mapper、Service、Controller等代码,大大提高开发效率。
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
public class CodeGenerator {
public static void main(String[] args) {
// 创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
gc.setAuthor("作者名");
gc.setOpen(false);
gc.setFileOverride(true);
gc.setActiveRecord(true);
gc.setEnableCache(false);
gc.setBaseResultMap(true);
gc.setBaseColumnList(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("password");
mpg.setDataSource(ddsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("user");
pc.setParent("com.example.demo");
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude("user", "role", "user_role");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
// 执行生成
mpg.execute();
}
}
2.2 自定义代码生成模板
为了满足特定需求,可以自定义代码生成模板:
// 自定义模板配置
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setEntity("/templates/entity.java");
templateConfig.setMapper("/templates/mapper.java");
templateConfig.setService("/templates/service.java");
templateConfig.setServiceImpl("/templates/serviceImpl.java");
templateConfig.setController("/templates/controller.java");
mpg.setTemplate(templateConfig);
三、数据库连接池优化
3.1 HikariCP配置详解
HikariCP是目前性能最好的连接池之一,合理的配置对系统性能至关重要:
spring:
datasource:
hikari:
# 连接池大小配置
maximum-pool-size: 20
minimum-idle: 5
# 连接超时配置
connection-timeout: 30000
# 空闲连接超时
idle-timeout: 600000
# 连接最大生命周期
max-lifetime: 1800000
# 连接测试
validation-timeout: 5000
# 连接池名称
pool-name: MyHikariCP
# 自动提交
auto-commit: true
# 连接属性
connection-properties:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
3.2 连接池监控
@Configuration
public class DataSourceConfig {
@Bean
@Primary
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
// 添加监控
config.setRegisterMbeans(true);
config.setPoolName("MyHikariCP");
return new HikariDataSource(config);
}
}
四、MyBatis Plus核心功能使用
4.1 基础CRUD操作
// User实体类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String username;
private String email;
private Integer status;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
// Mapper接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 自定义查询方法
List<User> selectByStatus(@Param("status") Integer status);
}
// Service实现
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUserByStatus(Integer status) {
return this.baseMapper.selectByStatus(status);
}
@Override
public Page<User> getUserPage(int current, int size) {
Page<User> page = new Page<>(current, size);
return this.baseMapper.selectPage(page, null);
}
}
4.2 条件构造器使用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 复杂查询示例
public List<User> searchUsers() {
return userMapper.selectList(new QueryWrapper<User>()
.select("id", "username", "email")
.eq("status", 1)
.like("username", "admin")
.between("create_time", startDate, endDate)
.orderByDesc("create_time")
.last("limit 10"));
}
// 分页查询
public IPage<User> getUserPage(int current, int size) {
Page<User> page = new Page<>(current, size);
return userMapper.selectPage(page, new QueryWrapper<User>()
.eq("status", 1)
.orderByDesc("create_time"));
}
// 多表关联查询
public List<User> getUserWithRole() {
return userMapper.selectJoinList(new QueryWrapper<User>()
.select("u.id", "u.username", "r.role_name")
.from("user u")
.leftJoin("user_role ur on u.id = ur.user_id")
.leftJoin("role r on ur.role_id = r.id")
.eq("u.status", 1));
}
}
五、SQL优化策略
5.1 索引优化
-- 创建用户表索引
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`email` varchar(100) NOT NULL COMMENT '邮箱',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_username` (`username`),
KEY `idx_email` (`email`),
KEY `idx_status_create_time` (`status`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 复合索引优化查询
SELECT * FROM user WHERE status = 1 AND create_time >= '2023-01-01';
5.2 查询优化实践
@Service
public class OptimizedUserService {
@Autowired
private UserMapper userMapper;
// 优化前:全表扫描
public List<User> getActiveUsers() {
return userMapper.selectList(new QueryWrapper<User>()
.eq("status", 1));
}
// 优化后:使用索引字段查询
public List<User> getActiveUsersByDate(Date startDate) {
return userMapper.selectList(new QueryWrapper<User>()
.eq("status", 1)
.ge("create_time", startDate)
.orderByDesc("create_time"));
}
// 使用select指定字段,避免全字段查询
public List<UserDTO> getUserDTOList() {
return userMapper.selectJoinList(new QueryWrapper<User>()
.select("u.id", "u.username", "u.email", "r.role_name")
.from("user u")
.leftJoin("user_role ur on u.id = ur.user_id")
.leftJoin("role r on ur.role_id = r.id")
.eq("u.status", 1)
.orderByDesc("u.create_time"));
}
}
5.3 批量操作优化
@Service
public class BatchUserService {
@Autowired
private UserMapper userMapper;
// 批量插入优化
public void batchInsertUsers(List<User> users) {
// 使用批量插入
userMapper.insertBatchSomeColumn(users);
}
// 批量更新优化
public void batchUpdateUsers(List<User> users) {
// 使用批量更新
userMapper.updateBatchById(users);
}
// 分批处理大数据量
public void processLargeData(List<User> allUsers) {
int batchSize = 1000;
for (int i = 0; i < allUsers.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, allUsers.size());
List<User> batch = allUsers.subList(i, endIndex);
userMapper.insertBatchSomeColumn(batch);
}
}
}
六、缓存策略实现
6.1 Redis缓存集成
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 2000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
6.2 缓存注解使用
@Service
public class CachedUserService {
@Autowired
private UserMapper userMapper;
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
return userMapper.selectById(id);
}
@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
userMapper.updateById(user);
}
@CacheEvict(value = "users", allEntries = true)
public void clearUserCache() {
// 清除所有用户缓存
}
// 复合缓存策略
@Cacheable(value = "userList", key = "#status + '_' + #current + '_' + #size")
public IPage<User> getUserPageByStatus(Integer status, int current, int size) {
Page<User> page = new Page<>(current, size);
return userMapper.selectPage(page, new QueryWrapper<User>()
.eq("status", status)
.orderByDesc("create_time"));
}
}
6.3 自定义缓存管理
@Component
public class UserCacheManager {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String USER_CACHE_KEY = "user:";
private static final String USER_LIST_CACHE_KEY = "user_list:";
public User getUserFromCache(Long userId) {
String key = USER_CACHE_KEY + userId;
return (User) redisTemplate.opsForValue().get(key);
}
public void putUserToCache(Long userId, User user) {
String key = USER_CACHE_KEY + userId;
redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
}
public void removeUserFromCache(Long userId) {
String key = USER_CACHE_KEY + userId;
redisTemplate.delete(key);
}
public List<User> getUserListFromCache(String key) {
return (List<User>) redisTemplate.opsForValue().get(key);
}
public void putUserListToCache(String key, List<User> users) {
redisTemplate.opsForValue().set(key, users, 10, TimeUnit.MINUTES);
}
}
七、事务管理最佳实践
7.1 事务配置
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
TransactionTemplate template = new TransactionTemplate();
template.setTransactionManager(transactionManager);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
template.setTimeout(30);
return template;
}
}
7.2 事务使用示例
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
// 复杂业务事务
public void createUserWithRole(User user, Long roleId) {
// 保存用户
userMapper.insert(user);
// 保存用户角色关联
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(roleId);
// 这里可以添加更多业务逻辑
userRoleMapper.insert(userRole);
}
// 事务回滚示例
@Transactional(rollbackFor = Exception.class)
public void transferMoney(Long fromUserId, Long toUserId, BigDecimal amount) {
try {
User fromUser = userMapper.selectById(fromUserId);
User toUser = userMapper.selectById(toUserId);
if (fromUser.getBalance().compareTo(amount) < 0) {
throw new RuntimeException("余额不足");
}
fromUser.setBalance(fromUser.getBalance().subtract(amount));
toUser.setBalance(toUser.getBalance().add(amount));
userMapper.updateById(fromUser);
userMapper.updateById(toUser);
} catch (Exception e) {
// 事务会自动回滚
throw new RuntimeException("转账失败", e);
}
}
}
八、性能监控与调优
8.1 SQL监控配置
mybatis-plus:
configuration:
# 开启SQL日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 开启SQL性能分析
safe-row-count: true
# 开启缓存
cache-enabled: true
8.2 自定义性能监控
@Component
public class PerformanceMonitor {
private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitor.class);
@EventListener
public void handleQueryEvent(QueryEvent event) {
long startTime = event.getStartTime();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
if (duration > 1000) { // 超过1秒的查询记录日志
logger.warn("Slow query detected: {}ms, SQL: {}", duration, event.getSql());
}
}
// 监控方法执行时间
public <T> T monitorExecution(String methodName, Supplier<T> supplier) {
long startTime = System.currentTimeMillis();
try {
T result = supplier.get();
long endTime = System.currentTimeMillis();
logger.info("Method {} executed in {}ms", methodName, (endTime - startTime));
return result;
} catch (Exception e) {
long endTime = System.currentTimeMillis();
logger.error("Method {} failed after {}ms", methodName, (endTime - startTime), e);
throw e;
}
}
}
九、异常处理与日志记录
9.1 统一异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
logger.warn("Business exception: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(e.getCode(), e.getMessage()));
}
@ExceptionHandler(DataAccessException.class)
public ResponseEntity<ErrorResponse> handleDataAccessException(DataAccessException e) {
logger.error("Database access error", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(500, "数据库访问错误"));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
logger.error("Unexpected error", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(500, "系统内部错误"));
}
}
// 错误响应类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorResponse {
private int code;
private String message;
private long timestamp = System.currentTimeMillis();
}
9.2 日志配置
logging:
level:
com.example.demo: DEBUG
org.springframework.web: DEBUG
org.mybatis: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file:
name: logs/app.log
十、部署与运维最佳实践
10.1 Docker部署配置
# Dockerfile
FROM openjdk:11-jre-slim
WORKDIR /app
COPY target/demo-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- mysql
- redis
restart: unless-stopped
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: mydb
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
mysql_data:
redis_data:
10.2 健康检查
@RestController
public class HealthController {
@Autowired
private DataSource dataSource;
@GetMapping("/health")
public ResponseEntity<HealthStatus> health() {
try {
Connection connection = dataSource.getConnection();
boolean isHealthy = !connection.isClosed();
connection.close();
return ResponseEntity.ok(new HealthStatus(isHealthy, "Database connection OK"));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new HealthStatus(false, "Database connection failed"));
}
}
}
@Data
@AllArgsConstructor
public class HealthStatus {
private boolean healthy;
private String message;
}
结语
通过本文的详细介绍,我们全面介绍了Spring Boot与MyBatis Plus结合使用的最佳实践。从环境搭建、代码生成到数据库优化、缓存策略、事务管理等各个方面,都提供了详细的配置示例和实用建议。
在实际项目开发中,建议根据具体业务需求选择合适的技术方案。代码生成器可以大大提高开发效率,但也要注意在生成代码基础上进行适当的定制化开发。数据库优化是性能提升的关键,合理的索引设计和SQL优化能够显著提升系统性能。缓存策略的合理使用可以有效减轻数据库压力,但要注意缓存一致性问题。
最后,完善的异常处理机制和日志记录对于系统的稳定运行至关重要。通过合理的监控和运维配置,可以确保系统在生产环境中的稳定运行。
希望本文能够为Java后端开发者在使用Spring Boot和MyBatis Plus时提供有价值的参考,帮助大家构建更加高效、稳定的后端服务系统。

评论 (0)