Spring Boot + MyBatis Plus 最佳实践:从代码生成到数据库性能调优的全流程指南

Piper146
Piper146 2026-02-04T08:16:10+08:00
0 0 0

引言

在现代Java企业级应用开发中,Spring Boot与MyBatis Plus的组合已经成为主流技术栈。Spring Boot凭借其简化配置、快速启动的特点,而MyBatis Plus则通过强大的CRUD操作和代码生成能力,为开发者提供了高效的数据库访问解决方案。本文将从代码生成开始,深入探讨如何构建一个高性能、可维护的数据库访问层,涵盖从基础配置到性能优化的完整实践流程。

一、环境准备与基础配置

1.1 项目依赖配置

首先,我们需要在pom.xml中添加必要的依赖:

<dependencies>
    <!-- Spring Boot Starter -->
    <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>
    
    <!-- 数据库驱动 -->
    <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>
    
    <!-- Test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

1.2 数据库配置

application.yml中配置数据库连接:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
    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

mybatis-plus:
  configuration:
    # 开启驼峰命名转换
    map-underscore-to-camel-case: true
    # 日志配置
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: auto

二、代码生成器实践

2.1 MyBatis Plus代码生成器配置

MyBatis Plus提供了强大的代码生成器,可以快速生成实体类、Mapper接口、Service层等代码:

@Configuration
public class CodeGenerator {
    
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("developer");
        gc.setOpen(false);
        gc.setSwagger2(true); // 实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);
        
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
        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_info"); // 需要生成的表名
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        
        mpg.execute();
    }
}

2.2 生成的代码结构

通过代码生成器,我们得到以下结构:

// 实体类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user_info")
public class UserInfo implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    
    private String username;
    
    private String email;
    
    private LocalDateTime createTime;
}

// Mapper接口
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
    
}

// Service接口
public interface UserInfoService extends IService<UserInfo> {
    
}

// Service实现类
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
    
}

三、基础CRUD操作实践

3.1 基本查询操作

MyBatis Plus提供了丰富的查询方法:

@Service
public class UserInfoService {
    
    @Autowired
    private UserInfoMapper userInfoMapper;
    
    // 根据ID查询
    public UserInfo getUserById(Long id) {
        return userInfoMapper.selectById(id);
    }
    
    // 查询所有用户
    public List<UserInfo> getAllUsers() {
        return userInfoMapper.selectList(null);
    }
    
    // 条件查询
    public List<UserInfo> getUsersByCondition(String username, String email) {
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        if (StringUtils.hasText(username)) {
            wrapper.like("username", username);
        }
        if (StringUtils.hasText(email)) {
            wrapper.like("email", email);
        }
        return userInfoMapper.selectList(wrapper);
    }
    
    // 分页查询
    public IPage<UserInfo> getUsersByPage(int current, int size) {
        Page<UserInfo> page = new Page<>(current, size);
        return userInfoMapper.selectPage(page, null);
    }
}

3.2 动态条件构建

// 复杂条件查询示例
public IPage<UserInfo> getUsersWithComplexCondition(UserQueryDTO queryDTO) {
    Page<UserInfo> page = new Page<>(queryDTO.getCurrent(), queryDTO.getSize());
    
    QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
    
    // 时间范围查询
    if (queryDTO.getStartTime() != null && queryDTO.getEndTime() != null) {
        wrapper.between("create_time", queryDTO.getStartTime(), queryDTO.getEndTime());
    }
    
    // 状态查询
    if (queryDTO.getStatus() != null) {
        wrapper.eq("status", queryDTO.getStatus());
    }
    
    // 关键词搜索
    if (StringUtils.hasText(queryDTO.getKeyword())) {
        wrapper.like("username", queryDTO.getKeyword())
               .or()
               .like("email", queryDTO.getKeyword());
    }
    
    // 排序
    if (queryDTO.getSortField() != null) {
        if (queryDTO.getSortOrder() == SortOrder.DESC) {
            wrapper.orderByDesc(queryDTO.getSortField());
        } else {
            wrapper.orderByAsc(queryDTO.getSortField());
        }
    }
    
    return userInfoMapper.selectPage(page, wrapper);
}

四、SQL优化策略

4.1 索引优化实践

良好的索引设计是性能优化的基础:

-- 创建用户表时的索引优化
CREATE TABLE `user_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `email` varchar(100) NOT NULL COMMENT '邮箱',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),
  KEY `idx_email` (`email`),
  KEY `idx_status_create_time` (`status`,`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

4.2 避免N+1查询问题

// 错误示例:N+1查询
public List<UserInfo> getUsersWithOrders() {
    List<UserInfo> users = userInfoMapper.selectList(null);
    // 每次循环都会执行一次查询
    for (UserInfo user : users) {
        user.setOrders(orderService.getOrdersByUserId(user.getId()));
    }
    return users;
}

// 正确示例:使用关联查询
public List<UserInfo> getUsersWithOrdersOptimized() {
    // 使用自定义SQL或MyBatis的关联查询
    return userInfoMapper.selectUserWithOrders();
}

4.3 SQL执行分析

// 配置日志输出,监控慢SQL
@Configuration
public class MyBatisConfig {
    
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        // 设置日志实现
        Configuration configuration = new Configuration();
        configuration.setLogImpl(StdOutImpl.class);
        configuration.setMapUnderscoreToCamelCase(true);
        factoryBean.setConfiguration(configuration);
        return factoryBean.getObject();
    }
}

五、缓存策略实现

5.1 Redis集成配置

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

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
spring:
  redis:
    host: localhost
    port: 6379
    timeout: 2000ms
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
        min-idle: 5

5.2 缓存注解使用

@Service
public class UserInfoService {
    
    @Autowired
    private UserInfoMapper userInfoMapper;
    
    @Cacheable(value = "user", key = "#id")
    public UserInfo getUserById(Long id) {
        return userInfoMapper.selectById(id);
    }
    
    @CacheEvict(value = "user", key = "#user.id")
    public void updateUser(UserInfo user) {
        userInfoMapper.updateById(user);
    }
    
    @CacheEvict(value = "user", allEntries = true)
    public void clearUserCache() {
        // 清空所有用户缓存
    }
}

5.3 自定义缓存策略

@Component
public class UserCacheService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String USER_KEY_PREFIX = "user:";
    private static final int CACHE_EXPIRE_TIME = 3600; // 1小时
    
    public UserInfo getUserFromCache(Long userId) {
        String key = USER_KEY_PREFIX + userId;
        Object cached = redisTemplate.opsForValue().get(key);
        
        if (cached != null) {
            return (UserInfo) cached;
        }
        
        // 缓存未命中,查询数据库
        UserInfo user = userInfoMapper.selectById(userId);
        if (user != null) {
            redisTemplate.opsForValue().set(key, user, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
        }
        
        return user;
    }
    
    public void updateUserInCache(UserInfo user) {
        String key = USER_KEY_PREFIX + user.getId();
        redisTemplate.opsForValue().set(key, user, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
    }
}

六、事务管理最佳实践

6.1 声明式事务配置

@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {
    
    @Autowired
    private UserInfoMapper userInfoMapper;
    
    @Autowired
    private OrderMapper orderMapper;
    
    public void createUserWithOrder(UserInfo user, Order order) {
        // 保存用户
        userInfoMapper.insert(user);
        
        // 保存订单,关联用户ID
        order.setUserId(user.getId());
        orderMapper.insert(order);
        
        // 模拟业务异常
        if (user.getUsername().equals("error")) {
            throw new RuntimeException("模拟业务异常");
        }
    }
}

6.2 事务传播行为控制

@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private PaymentService paymentService;
    
    // 使用REQUIRED传播行为
    @Transactional(propagation = Propagation.REQUIRED)
    public void processOrder(Order order) {
        // 保存订单
        orderMapper.insert(order);
        
        // 处理支付,使用同一个事务
        paymentService.processPayment(order.getId(), order.getAmount());
    }
    
    // 使用REQUIRES_NEW传播行为
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment(Long orderId, BigDecimal amount) {
        // 处理支付逻辑
        Payment payment = new Payment();
        payment.setOrderId(orderId);
        payment.setAmount(amount);
        payment.setCreateTime(LocalDateTime.now());
        // 保存支付记录
    }
}

七、性能监控与调优

7.1 数据库连接池监控

@Configuration
public class DataSourceMonitorConfig {
    
    @Bean
    @Primary
    public DataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        // 配置参数
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_db");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        
        // 监控配置
        dataSource.setMaximumPoolSize(20);
        dataSource.setMinimumIdle(5);
        dataSource.setConnectionTimeout(30000);
        dataSource.setIdleTimeout(600000);
        dataSource.setMaxLifetime(1800000);
        
        return dataSource;
    }
}

7.2 SQL性能分析

@Component
public class SqlPerformanceMonitor {
    
    private static final Logger logger = LoggerFactory.getLogger(SqlPerformanceMonitor.class);
    
    @EventListener
    public void handleSqlExecution(SqlExecutionEvent event) {
        long executionTime = event.getExecutionTime();
        String sql = event.getSql();
        
        if (executionTime > 1000) { // 超过1秒的SQL记录日志
            logger.warn("Slow SQL detected: {} - Execution time: {}ms", sql, executionTime);
        }
    }
}

7.3 数据库查询优化建议

// 优化前:全表扫描
public List<UserInfo> getActiveUsers() {
    return userInfoMapper.selectList(new QueryWrapper<UserInfo>().eq("status", 1));
}

// 优化后:使用索引
public List<UserInfo> getActiveUsersOptimized() {
    // 确保在status字段上有索引
    return userInfoMapper.selectList(new QueryWrapper<UserInfo>().eq("status", 1));
}

// 分页查询优化
public IPage<UserInfo> getActiveUsersPaginated(int current, int size) {
    Page<UserInfo> page = new Page<>(current, size);
    
    // 使用索引字段进行排序
    page.setOrders(OrderItem.asc("create_time"));
    
    return userInfoMapper.selectPage(page, 
        new QueryWrapper<UserInfo>().eq("status", 1));
}

八、异常处理与日志记录

8.1 统一异常处理

@ControllerAdvice
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(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception e) {
        logger.error("Unexpected error occurred", e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ErrorResponse("500", "Internal server error"));
    }
}

@Data
@AllArgsConstructor
public class ErrorResponse {
    private String code;
    private String message;
}

8.2 操作日志记录

@Service
public class OperationLogService {
    
    @Autowired
    private OperationLogMapper operationLogMapper;
    
    public void logOperation(String operation, String description) {
        OperationLog log = new OperationLog();
        log.setOperation(operation);
        log.setDescription(description);
        log.setOperator(getCurrentUserName());
        log.setCreateTime(LocalDateTime.now());
        operationLogMapper.insert(log);
    }
    
    private String getCurrentUserName() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication != null ? authentication.getName() : "anonymous";
    }
}

九、安全与权限控制

9.1 数据权限控制

@Component
public class DataPermissionInterceptor implements InnerInterceptor {
    
    @Override
    public void beforeQuery(ExecutorWrapper executor, MappedStatement mappedStatement) {
        // 添加数据权限过滤条件
        String sql = executor.getSql();
        if (sql.contains("user_info")) {
            // 根据用户角色添加过滤条件
            String permissionFilter = buildPermissionFilter();
            // 将过滤条件添加到SQL中
        }
    }
    
    private String buildPermissionFilter() {
        // 构建数据权限过滤条件
        return " AND create_user_id = #{currentUserId}";
    }
}

9.2 SQL注入防护

// 使用MyBatis Plus的安全查询方法
public List<UserInfo> getUsersBySafeCondition(String username) {
    // 使用QueryWrapper的参数化查询,防止SQL注入
    QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
    wrapper.like("username", username);
    return userInfoMapper.selectList(wrapper);
}

// 避免使用字符串拼接
public List<UserInfo> getUsersByUnsafeCondition(String condition) {
    // 不推荐:容易造成SQL注入
    // String sql = "SELECT * FROM user_info WHERE username LIKE '%" + condition + "%'";
    
    // 推荐:使用参数化查询
    QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
    wrapper.like("username", condition);
    return userInfoMapper.selectList(wrapper);
}

十、总结与最佳实践

10.1 核心要点回顾

通过本文的详细介绍,我们掌握了Spring Boot + MyBatis Plus的最佳实践:

  1. 代码生成器:提高开发效率,减少重复代码
  2. SQL优化:合理设计索引,避免N+1问题
  3. 缓存策略:结合Redis实现高效的缓存管理
  4. 事务控制:正确使用事务传播行为保证数据一致性
  5. 性能监控:建立完善的监控体系及时发现性能瓶颈

10.2 实践建议

// 综合最佳实践示例
@Service
@Transactional
@CacheConfig(cacheNames = "user")
public class UserService {
    
    @Autowired
    private UserInfoMapper userInfoMapper;
    
    @Cacheable(key = "#id")
    public UserInfo getUserById(Long id) {
        return userInfoMapper.selectById(id);
    }
    
    @CacheEvict(key = "#user.id")
    public void updateUser(UserInfo user) {
        userInfoMapper.updateById(user);
    }
    
    // 高性能查询示例
    public IPage<UserInfo> getUsersByPage(int current, int size, String keyword) {
        Page<UserInfo> page = new Page<>(current, size);
        
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        if (StringUtils.hasText(keyword)) {
            wrapper.like("username", keyword)
                   .or()
                   .like("email", keyword);
        }
        
        // 使用索引字段排序
        page.setOrders(OrderItem.desc("create_time"));
        
        return userInfoMapper.selectPage(page, wrapper);
    }
}

10.3 持续改进方向

  • 监控体系完善:建立更全面的性能监控指标
  • 自动化测试:增加单元测试和集成测试覆盖率
  • 微服务架构:在分布式场景下的优化策略
  • 容器化部署:Docker环境下的配置优化

通过以上实践,我们可以构建出一个高性能、可维护的数据库访问层,为企业的业务系统提供稳定可靠的数据支持。在实际项目中,建议根据具体需求灵活应用这些技术点,并持续关注新技术的发展,不断优化系统性能。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000