Spring Boot 集成 HikariCP 完整示例教程

Spring Boot 集成 HikariCP 完整示例教程

适用版本:Spring Boot 3.x, HikariCP 5.x | 语言:Java 17+ | 数据库:MySQL 8.0

项目概述

本示例演示如何在Spring Boot项目中集成和优化HikariCP连接池,包含完整的配置、使用、监控和最佳实践。

1. 项目依赖配置

1.1 Maven依赖 (pom.xml)


<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.0modelVersion>
    
    <groupId>com.examplegroupId>
    <artifactId>hikaricp-demoartifactId>
    <version>1.0.0version>
    <packaging>jarpackaging>
    
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>3.2.0version>
        <relativePath/>
    parent>
    
    <properties>
        <java.version>17java.version>
        <hikaricp.version>5.1.0hikaricp.version>
    properties>
    
    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jpaartifactId>
        dependency>
        
        
        <dependency>
            <groupId>com.zaxxergroupId>
            <artifactId>HikariCPartifactId>
            <version>${hikaricp.version}version>
        dependency>
        
        
        <dependency>
            <groupId>com.mysqlgroupId>
            <artifactId>mysql-connector-jartifactId>
            <scope>runtimescope>
        dependency>
        
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        
        
        <dependency>
            <groupId>io.micrometergroupId>
            <artifactId>micrometer-registry-prometheusartifactId>
        dependency>
        
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>
project>

1.2 Gradle依赖 (build.gradle)

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.0'
    id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.example'
version = '1.0.0'
java.sourceCompatibility = JavaVersion.VERSION_17

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'com.zaxxer:HikariCP:5.1.0'
    implementation 'io.micrometer:micrometer-registry-prometheus'
    
    runtimeOnly 'com.mysql:mysql-connector-j'
    
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

2. HikariCP 配置

2.1 基础配置 (application.yml)

# application.yml
spring:
  application:
    name: hikaricp-demo
    
  # 数据源配置
  datasource:
    url: jdbc:mysql://localhost:3306/demo_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    
    # HikariCP 核心配置
    hikari:
      # 连接池名称
      pool-name: DemoHikariPool
      
      # 核心连接数配置
      minimum-idle: 10                    # 最小空闲连接数
      maximum-pool-size: 50               # 最大连接池大小
      
      # 超时配置
      connection-timeout: 30000           # 连接超时时间 (30秒)
      idle-timeout: 600000                # 空闲超时时间 (10分钟)
      max-lifetime: 1800000               # 连接最大生命周期 (30分钟)
      validation-timeout: 5000            # 连接验证超时时间 (5秒)
      
      # 连接测试
      connection-test-query: SELECT 1
      
      # 性能优化
      auto-commit: true                   # 自动提交
      read-only: false                    # 读写模式
      catalog: demo_db                    # 默认catalog
      
      # 连接泄漏检测
      leak-detection-threshold: 60000     # 泄漏检测阈值 (1分钟)
      
      # 连接初始化
      connection-init-sql: "SET NAMES utf8mb4"
      
      # 数据源属性
      data-source-properties:
        cachePrepStmts: true
        prepStmtCacheSize: 250
        prepStmtCacheSqlLimit: 2048
        useServerPrepStmts: true
        useLocalSessionState: true
        rewriteBatchedStatements: true
        cacheResultSetMetadata: true
        cacheServerConfiguration: true
        elideSetAutoCommits: true
        maintainTimeStats: false

  # JPA 配置
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
        format_sql: true

# 日志配置
logging:
  level:
    com.zaxxer.hikari.HikariConfig: DEBUG
    com.zaxxer.hikari.pool.HikariPool: INFO
    com.zaxxer.hikari.pool.ProxyConnection: WARN
    org.hibernate.SQL: DEBUG
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE

# Actuator 配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,hikaricp,loggers,prometheus
  endpoint:
    health:
      show-details: always
  metrics:
    export:
      prometheus:
        enabled: true

# 服务端口
server:
  port: 8080

2.2 环境差异化配置

# application-dev.yml (开发环境)
spring:
  datasource:
    hikari:
      minimum-idle: 5
      maximum-pool-size: 20
      leak-detection-threshold: 30000

logging:
  level:
    com.zaxxer.hikari: DEBUG

---
# application-prod.yml (生产环境)
spring:
  datasource:
    hikari:
      minimum-idle: 20
      maximum-pool-size: 100
      connection-timeout: 20000
      leak-detection-threshold: 120000

logging:
  level:
    com.zaxxer.hikari.pool.HikariPool: INFO
    com.zaxxer.hikari.pool.ProxyConnection: WARN

3. Java 代码实现

3.1 主启动类

package com.example.hikaridemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * HikariCP Demo 应用启动类
 * @author Demo Team
 * @version 1.0.0
 */
@SpringBootApplication
@EnableScheduling
public class HikariDemoApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(HikariDemoApplication.class, args);
        System.out.println(" HikariCP Demo 应用启动成功!");
        System.out.println(" 访问监控端点: http://localhost:8080/actuator/hikaricp");
        System.out.println(" 访问健康检查: http://localhost:8080/actuator/health");
    }
}

3.2 数据库实体类

package com.example.hikaridemo.entity;

import jakarta.persistence.*;
import java.time.LocalDateTime;

/**
 * 用户实体类
 */
@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true, length = 50)
    private String username;
    
    @Column(nullable = false, length = 100)
    private String email;
    
    @Column(name = "full_name", length = 100)
    private String fullName;
    
    @Column(name = "created_at")
    private LocalDateTime createdAt;
    
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
    
    // 构造函数
    public User() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
    
    public User(String username, String email, String fullName) {
        this();
        this.username = username;
        this.email = email;
        this.fullName = fullName;
    }
    
    // Getter 和 Setter 方法
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public String getFullName() { return fullName; }
    public void setFullName(String fullName) { this.fullName = fullName; }
    
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
    
    public LocalDateTime getUpdatedAt() { return updatedAt; }
    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
    
    @PreUpdate
    public void preUpdate() {
        this.updatedAt = LocalDateTime.now();
    }
    
    @Override
    public String toString() {
        return "User{id=" + id + ", username='" + username + 
               "', email='" + email + "', fullName='" + fullName + "'}";
    }
}

3.3 数据访问层

package com.example.hikaridemo.repository;

import com.example.hikaridemo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
 * 用户数据访问接口
 */
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    /**
     * 根据用户名查找用户
     */
    Optional<User> findByUsername(String username);
    
    /**
     * 根据邮箱查找用户
     */
    Optional<User> findByEmail(String email);
    
    /**
     * 模糊查询用户
     */
    @Query("SELECT u FROM User u WHERE u.fullName LIKE %:name% OR u.username LIKE %:name%")
    List<User> findUsersByNameContaining(@Param("name") String name);
    
    /**
     * 统计用户总数
     */
    @Query("SELECT COUNT(u) FROM User u")
    Long countTotalUsers();
}

3.4 业务服务层

package com.example.hikaridemo.service;

import com.example.hikaridemo.entity.User;
import com.example.hikaridemo.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

/**
 * 用户业务服务类
 */
@Service
@Transactional
public class UserService {
    
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);
    
    @Autowired
    private UserRepository userRepository;
    
    /**
     * 创建用户
     */
    public User createUser(String username, String email, String fullName) {
        logger.info("创建新用户: username={}, email={}", username, email);
        
        // 检查用户名是否已存在
        if (userRepository.findByUsername(username).isPresent()) {
            throw new RuntimeException("用户名已存在: " + username);
        }
        
        // 检查邮箱是否已存在
        if (userRepository.findByEmail(email).isPresent()) {
            throw new RuntimeException("邮箱已存在: " + email);
        }
        
        User user = new User(username, email, fullName);
        User savedUser = userRepository.save(user);
        
        logger.info("用户创建成功: {}", savedUser);
        return savedUser;
    }
    
    /**
     * 根据ID查找用户
     */
    @Transactional(readOnly = true)
    public Optional<User> findUserById(Long id) {
        logger.debug("查找用户: id={}", id);
        return userRepository.findById(id);
    }
    
    /**
     * 根据用户名查找用户
     */
    @Transactional(readOnly = true)
    public Optional<User> findUserByUsername(String username) {
        logger.debug("查找用户: username={}", username);
        return userRepository.findByUsername(username);
    }
    
    /**
     * 获取所有用户
     */
    @Transactional(readOnly = true)
    public List<User> getAllUsers() {
        logger.debug("获取所有用户列表");
        return userRepository.findAll();
    }
    
    /**
     * 模糊搜索用户
     */
    @Transactional(readOnly = true)
    public List<User> searchUsers(String keyword) {
        logger.debug("搜索用户: keyword={}", keyword);
        return userRepository.findUsersByNameContaining(keyword);
    }
    
    /**
     * 更新用户信息
     */
    public User updateUser(Long id, String email, String fullName) {
        logger.info("更新用户信息: id={}", id);
        
        User user = userRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("用户不存在: " + id));
        
        if (email != null) {
            user.setEmail(email);
        }
        if (fullName != null) {
            user.setFullName(fullName);
        }
        
        User updatedUser = userRepository.save(user);
        logger.info("用户信息更新成功: {}", updatedUser);
        return updatedUser;
    }
    
    /**
     * 删除用户
     */
    public void deleteUser(Long id) {
        logger.info("删除用户: id={}", id);
        
        if (!userRepository.existsById(id)) {
            throw new RuntimeException("用户不存在: " + id);
        }
        
        userRepository.deleteById(id);
        logger.info("用户删除成功: id={}", id);
    }
    
    /**
     * 获取用户总数
     */
    @Transactional(readOnly = true)
    public Long getTotalUserCount() {
        return userRepository.countTotalUsers();
    }
}

3.5 REST 控制器

package com.example.hikaridemo.controller;

import com.example.hikaridemo.entity.User;
import com.example.hikaridemo.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 用户管理 REST API 控制器
 */
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "*")
public class UserController {
    
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    
    @Autowired
    private UserService userService;
    
    /**
     * 创建用户
     */
    @PostMapping
    public ResponseEntity<?> createUser(@RequestBody Map<String, String> request) {
        try {
            String username = request.get("username");
            String email = request.get("email");
            String fullName = request.get("fullName");
            
            if (username == null || email == null) {
                return ResponseEntity.badRequest()
                    .body(Map.of("error", "用户名和邮箱不能为空"));
            }
            
            User user = userService.createUser(username, email, fullName);
            return ResponseEntity.status(HttpStatus.CREATED).body(user);
            
        } catch (Exception e) {
            logger.error("创建用户失败", e);
            return ResponseEntity.badRequest()
                .body(Map.of("error", e.getMessage()));
        }
    }
    
    /**
     * 获取用户列表
     */
    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        try {
            List<User> users = userService.getAllUsers();
            return ResponseEntity.ok(users);
        } catch (Exception e) {
            logger.error("获取用户列表失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    /**
     * 根据ID获取用户
     */
    @GetMapping("/{id}")
    public ResponseEntity<?> getUserById(@PathVariable Long id) {
        try {
            Optional<User> user = userService.findUserById(id);
            if (user.isPresent()) {
                return ResponseEntity.ok(user.get());
            } else {
                return ResponseEntity.notFound().build();
            }
        } catch (Exception e) {
            logger.error("获取用户失败: id={}", id, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Map.of("error", e.getMessage()));
        }
    }
    
    /**
     * 搜索用户
     */
    @GetMapping("/search")
    public ResponseEntity<List<User>> searchUsers(@RequestParam String keyword) {
        try {
            List<User> users = userService.searchUsers(keyword);
            return ResponseEntity.ok(users);
        } catch (Exception e) {
            logger.error("搜索用户失败: keyword={}", keyword, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    /**
     * 更新用户
     */
    @PutMapping("/{id}")
    public ResponseEntity<?> updateUser(@PathVariable Long id, 
                                      @RequestBody Map<String, String> request) {
        try {
            String email = request.get("email");
            String fullName = request.get("fullName");
            
            User user = userService.updateUser(id, email, fullName);
            return ResponseEntity.ok(user);
            
        } catch (Exception e) {
            logger.error("更新用户失败: id={}", id, e);
            return ResponseEntity.badRequest()
                .body(Map.of("error", e.getMessage()));
        }
    }
    
    /**
     * 删除用户
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteUser(@PathVariable Long id) {
        try {
            userService.deleteUser(id);
            return ResponseEntity.ok(Map.of("message", "用户删除成功"));
        } catch (Exception e) {
            logger.error("删除用户失败: id={}", id, e);
            return ResponseEntity.badRequest()
                .body(Map.of("error", e.getMessage()));
        }
    }
    
    /**
     * 获取用户统计信息
     */
    @GetMapping("/stats")
    public ResponseEntity<Map<String, Object>> getUserStats() {
        try {
            Long totalUsers = userService.getTotalUserCount();
            return ResponseEntity.ok(Map.of(
                "totalUsers", totalUsers,
                "timestamp", System.currentTimeMillis()
            ));
        } catch (Exception e) {
            logger.error("获取用户统计失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

4. HikariCP 监控与管理

4.1 自定义监控组件

package com.example.hikaridemo.monitoring;

import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * HikariCP 连接池监控组件
 */
@Component
public class HikariPoolMonitor implements HealthIndicator {
    
    private static final Logger logger = LoggerFactory.getLogger(HikariPoolMonitor.class);
    
    @Autowired
    private DataSource dataSource;
    
    /**
     * 健康检查
     */
    @Override
    public Health health() {
        try {
            if (dataSource instanceof HikariDataSource) {
                HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
                HikariPoolMXBean poolBean = hikariDataSource.getHikariPoolMXBean();
                
                int totalConnections = poolBean.getTotalConnections();
                int activeConnections = poolBean.getActiveConnections();
                int idleConnections = poolBean.getIdleConnections();
                int threadsAwaitingConnection = poolBean.getThreadsAwaitingConnection();
                
                // 计算连接池使用率
                double utilizationRate = totalConnections > 0 ? 
                    (double) activeConnections / totalConnections : 0;
                
                Health.Builder builder = Health.up()
                    .withDetail("poolName", hikariDataSource.getPoolName())
                    .withDetail("totalConnections", totalConnections)
                    .withDetail("activeConnections", activeConnections)
                    .withDetail("idleConnections", idleConnections)
                    .withDetail("threadsAwaitingConnection", threadsAwaitingConnection)
                    .withDetail("utilizationRate", String.format("%.2f%%", utilizationRate * 100));
                
                // 根据使用率判断健康状态
                if (utilizationRate > 0.9) {
                    builder.status("WARNING").withDetail("message", "连接池使用率过高");
                } else if (threadsAwaitingConnection > 5) {
                    builder.status("WARNING").withDetail("message", "等待连接的线程过多");
                }
                
                return builder.build();
            }
            
            return Health.down().withDetail("error", "数据源不是HikariDataSource类型").build();
            
        } catch (Exception e) {
            logger.error("HikariCP健康检查失败", e);
            return Health.down().withDetail("error", e.getMessage()).build();
        }
    }
    
    /**
     * 定时监控连接池状态
     */
    @Scheduled(fixedRate = 30000) // 每30秒执行一次
    public void monitorPoolStatus() {
        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
            HikariPoolMXBean poolBean = hikariDataSource.getHikariPoolMXBean();
            
            int totalConnections = poolBean.getTotalConnections();
            int activeConnections = poolBean.getActiveConnections();
            int idleConnections = poolBean.getIdleConnections();
            int threadsAwaitingConnection = poolBean.getThreadsAwaitingConnection();
            
            logger.info("HikariCP状态监控 - 总连接数:{}, 活跃连接:{}, 空闲连接:{}, 等待线程:{}", 
                totalConnections, activeConnections, idleConnections, threadsAwaitingConnection);
            
            // 告警逻辑
            double utilizationRate = totalConnections > 0 ? 
                (double) activeConnections / totalConnections : 0;
            
            if (utilizationRate > 0.9) {
                logger.warn("⚠️ 连接池使用率过高: {:.2f}%", utilizationRate * 100);
            }
            
            if (threadsAwaitingConnection > 5) {
                logger.warn("⚠️ 等待连接的线程过多: {}", threadsAwaitingConnection);
            }
        }
    }
}

4.2 连接池配置管理器

package com.example.hikaridemo.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

/**
 * HikariCP 数据源配置类
 */
@Configuration
public class HikariDataSourceConfig {
    
    private static final Logger logger = LoggerFactory.getLogger(HikariDataSourceConfig.class);
    
    @Value("${spring.datasource.url}")
    private String jdbcUrl;
    
    @Value("${spring.datasource.username}")
    private String username;
    
    @Value("${spring.datasource.password}")
    private String password;
    
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    
    /**
     * 创建 HikariCP 数据源
     */
    @Bean
    @Primary
    public DataSource primaryDataSource() {
        logger.info("正在初始化 HikariCP 数据源...");
        
        HikariConfig config = new HikariConfig();
        
        // 基础连接配置
        config.setJdbcUrl(jdbcUrl);
        config.setUsername(username);
        config.setPassword(password);
        config.setDriverClassName(driverClassName);
        
        // 连接池配置
        config.setPoolName("PrimaryHikariPool");
        config.setMinimumIdle(10);
        config.setMaximumPoolSize(50);
        
        // 超时配置
        config.setConnectionTimeout(30000);
        config.setIdleTimeout(600000);
        config.setMaxLifetime(1800000);
        config.setValidationTimeout(5000);
        
        // 连接测试
        config.setConnectionTestQuery("SELECT 1");
        
        // 性能优化配置
        config.setAutoCommit(true);
        config.setReadOnly(false);
        config.setCatalog("demo_db");
        
        // 泄漏检测
        config.setLeakDetectionThreshold(60000);
        
        // 数据源属性
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        config.addDataSourceProperty("useServerPrepStmts", "true");
        config.addDataSourceProperty("rewriteBatchedStatements", "true");
        
        HikariDataSource dataSource = new HikariDataSource(config);
        
        logger.info("✅ HikariCP 数据源初始化完成");
        logger.info(" 连接池配置 - 最小空闲:{}, 最大连接:{}", 
            config.getMinimumIdle(), config.getMaximumPoolSize());
        
        return dataSource;
    }
}

5. 测试用例

5.1 集成测试

package com.example.hikaridemo;

import com.example.hikaridemo.entity.User;
import com.example.hikaridemo.repository.UserRepository;
import com.example.hikaridemo.service.UserService;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * HikariCP 集成测试
 */
@SpringBootTest
@ActiveProfiles("test")
@Transactional
class HikariDemoApplicationTests {
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void testHikariDataSourceConfiguration() {
        // 验证数据源类型
        assertThat(dataSource).isInstanceOf(HikariDataSource.class);
        
        HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
        
        // 验证连接池配置
        assertThat(hikariDataSource.getPoolName()).contains("HikariPool");
        assertThat(hikariDataSource.getMaximumPoolSize()).isGreaterThan(0);
        assertThat(hikariDataSource.getMinimumIdle()).isGreaterThanOrEqualTo(0);
        
        System.out.println("✅ HikariCP 配置验证通过");
        System.out.println(" 连接池名称: " + hikariDataSource.getPoolName());
        System.out.println(" 最大连接数: " + hikariDataSource.getMaximumPoolSize());
        System.out.println(" 最小空闲连接: " + hikariDataSource.getMinimumIdle());
    }
    
    @Test
    void testDatabaseConnection() throws Exception {
        // 测试数据库连接
        try (var connection = dataSource.getConnection()) {
            assertThat(connection).isNotNull();
            assertThat(connection.isValid(5)).isTrue();
            
            // 执行简单查询
            try (var statement = connection.createStatement();
                 var resultSet = statement.executeQuery("SELECT 1 as test")) {
                assertThat(resultSet.next()).isTrue();
                assertThat(resultSet.getInt("test")).isEqualTo(1);
            }
        }
        
        System.out.println("✅ 数据库连接测试通过");
    }
    
    @Test
    void testUserCrudOperations() {
        // 创建用户
        User user = userService.createUser("testuser", "[email protected]", "Test User");
        assertThat(user.getId()).isNotNull();
        assertThat(user.getUsername()).isEqualTo("testuser");
        
        // 查询用户
        Optional<User> foundUser = userService.findUserById(user.getId());
        assertThat(foundUser).isPresent();
        assertThat(foundUser.get().getEmail()).isEqualTo("[email protected]");
        
        // 更新用户
        User updatedUser = userService.updateUser(user.getId(), "[email protected]", "Updated User");
        assertThat(updatedUser.getEmail()).isEqualTo("[email protected]");
        assertThat(updatedUser.getFullName()).isEqualTo("Updated User");
        
        // 搜索用户
        List<User> searchResults = userService.searchUsers("Updated");
        assertThat(searchResults).isNotEmpty();
        assertThat(searchResults.get(0).getFullName()).contains("Updated");
        
        // 删除用户
        userService.deleteUser(user.getId());
        Optional<User> deletedUser = userService.findUserById(user.getId());
        assertThat(deletedUser).isEmpty();
        
        System.out.println("✅ 用户CRUD操作测试通过");
    }
    
    @Test
    void testConnectionPoolPerformance() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        
        // 并发获取连接测试
        Thread[] threads = new Thread[20];
        for (int i = 0; i < threads.length; i++) {
            final int threadIndex = i;
            threads[i] = new Thread(() -> {
                try {
                    for (int j = 0; j < 10; j++) {
                        userService.createUser("user" + threadIndex + "_" + j, 
                            "user" + threadIndex + "_" + j + "@example.com", 
                            "User " + threadIndex + "_" + j);
                    }
                } catch (Exception e) {
                    // 忽略重复键异常
                }
            });
        }
        
        for (Thread thread : threads) {
            thread.start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        
        System.out.println("✅ 连接池性能测试完成");
        System.out.println(" 20个线程并发执行耗时: " + duration + "ms");
        
        // 验证数据
        Long totalUsers = userService.getTotalUserCount();
        System.out.println(" 创建用户总数: " + totalUsers);
        
        assertThat(duration).isLessThan(10000); // 应该在10秒内完成
    }
}

6. 运行和使用指南

6.1 数据库准备

-- 创建数据库
CREATE DATABASE demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用数据库
USE demo_db;

-- 用户表会由JPA自动创建,也可以手动创建
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL,
    full_name VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

6.2 启动应用

# 1. 克隆或创建项目
mkdir hikaricp-demo && cd hikaricp-demo

# 2. 构建项目
mvn clean compile

# 3. 启动应用
mvn spring-boot:run

# 或者使用Gradle
./gradlew bootRun

6.3 API 测试示例

# 1. 创建用户
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"username":"john_doe","email":"[email protected]","fullName":"John Doe"}'

# 2. 获取所有用户
curl http://localhost:8080/api/users

# 3. 根据ID获取用户
curl http://localhost:8080/api/users/1

# 4. 搜索用户
curl "http://localhost:8080/api/users/search?keyword=john"

# 5. 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","fullName":"John Updated"}'

# 6. 获取用户统计
curl http://localhost:8080/api/users/stats

# 7. 查看连接池状态
curl http://localhost:8080/actuator/hikaricp

# 8. 查看健康检查
curl http://localhost:8080/actuator/health

# 9. 查看Prometheus指标
curl http://localhost:8080/actuator/prometheus

7. 最佳实践总结

7.1 性能优化建议

  1. 连接池大小调优:根据应用并发量合理设置 maximumPoolSize
  2. 超时配置优化:避免设置过长的超时时间
  3. 连接验证策略:在高并发场景下谨慎使用连接验证
  4. MySQL驱动优化:启用prepared statement缓存等特性

7.2 监控和告警

  1. 关键指标监控:连接池使用率、等待线程数、连接获取耗时
  2. 日志分析:定期分析HikariCP日志,发现潜在问题
  3. 健康检查:集成Spring Boot Actuator进行健康监控

7.3 故障排查

  1. 连接泄漏:启用泄漏检测,定期检查未关闭的连接
  2. 性能问题:通过日志分析慢查询和连接获取延迟
  3. 配置调优:根据实际负载动态调整连接池参数

恭喜! 您已经成功创建了一个完整的Spring Boot + HikariCP示例应用。这个示例包含了从基础配置到高级监控的所有功能,可以作为生产环境的参考模板。

你可能感兴趣的:(Spring Boot 集成 HikariCP 完整示例教程)