Spring Boot + MyBatis Plus 最佳实践:从代码生成到数据库优化的完整指南

RedDust
RedDust 2026-02-28T17:05:00+08:00
0 0 0

灵活# 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)

    0/2000