ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SpringBoot+JDBC连接MySQL实战:从配置到避坑全指南

SpringBoot+JDBC连接MySQL实战:从配置到避坑全指南

简介:本资源是一套面向Java初学者与SpringBoot入门开发者的MySQL数据库集成实战方案,聚焦JDBC原生方式连接与操作MySQL的完整实现路径,覆盖环境搭建、驱动配置、连接池管理、CRUD编码及常见异常处理等核心环节。压缩包共136个文件,包含92个DLL动态库(含Oracle/MySQL本地驱动及依赖组件)、3个SQL建表与初始化脚本、3个Java源码文件(含JdbcController与User实体类)、6个XML配置文件(Spring Boot数据源与MyBatis相关)、4个EXE安装工具及3个MSI数据库服务安装包,辅以PDF文档、properties配置示例和HTML说明页,整体容量达637.42MB,结构完整、开箱即用。目前已有601人学习下载,资源提供可直接运行的Spring Boot工程(含编译后class文件与启动类)、配套MySQL安装介质、本地化驱动库及典型错误排查提示,特别适合缺乏数据库实战经验、需从零构建稳定连接能力的学习者快速上手并理解底层交互逻辑。

1. SpringBoot + JDBC 连 MySQL:不是配个 URL 就能查出数据的黑匣子

你是不是也遇到过:SpringBoot 项目里application.yml写好了spring.datasource.url,启动日志显示 “Started Application”,可一调JdbcTemplate.queryForObject()就抛CannotGetJdbcConnectionException?或者更玄学的是——本地跑得好好的,打包成 jar 丢到测试机上,连数据库超时都不报,直接卡死在DataSourceHealthIndicator?这不是环境问题,是 JDBC 这层薄薄的抽象底下,藏着至少 5 类配置冲突、3 种驱动加载时机陷阱、还有 MySQL 8.0+ 默认 strict mode 对空值和时间戳的“道德审判”。这份整套解决方案不讲 SpringBoot 自动装配原理,只给你能立刻粘贴、改两行就跑通的源码包(含完整pom.xml依赖树)、MySQL 5.7/8.0 双版本安装包(Windows/Linux 二进制免编译版)、以及我在线上压测时被反向教育过的 4 个关键参数——比如useSSL=false在 MySQL 8.0.28+ 已失效,必须换成enabledTLSProtocols=TLSv1.2。适合正在赶需求、没时间啃官方文档的 Java 后端,也适合刚学完 JDBC 基础、第一次把Connection塞进 Spring 容器的新手。


2. 从零建库到 SpringBoot 连通:三步闭环验证法

2.1 MySQL 安装包实操:为什么推荐二进制版而非 MSI?

网络上大量教程默认用 Windows MSI 安装器,但实际落地时你会发现:MSI 安装后服务名固定为MySQL80,而某些国产中间件监控系统硬编码扫描mysqld进程名;更致命的是,MSI 默认开启validate_password插件,导致你执行CREATE USER 'dev'@'%' IDENTIFIED BY '123';直接报错Your password does not satisfy the current policy requirements,新手根本找不到策略配置在哪。我们提供的安装包是官方二进制压缩包(mysql-5.7.42-linux-glibc2.12-x86_64.tar.gz/mysql-8.0.33-linux-glibc2.12-x86_64.tar.gz),解压即用,关键步骤如下:

# Linux 下解压并初始化(以 8.0.33 为例) tar -xzf mysql-8.0.33-linux-glibc2.12-x86_64.tar.gz mv mysql-8.0.33-linux-glibc2.12-x86_64 /opt/mysql8 mkdir -p /opt/mysql8/data /opt/mysql8/logs # 初始化(生成 root 临时密码,注意看 stdout 最后一行) /opt/mysql8/bin/mysqld --initialize --user=root --datadir=/opt/mysql8/data --log-error=/opt/mysql8/logs/error.log # 启动服务(不依赖 systemd,避免权限问题) /opt/mysql8/bin/mysqld_safe --defaults-file=/opt/mysql8/my.cnf &

提示:my.cnf必须显式指定,否则mysqld_safe会读取/etc/my.cnf导致配置混乱。我们包内已预置安全配置模板,关键项包括skip-grant-tables(仅首次初始化启用)、default_authentication_plugin=mysql_native_password(兼容老驱动)、sql_mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO(明确声明严格模式,避免上线后行为突变)。

2.2 创建开发专用用户:绕过 MySQL 8.0 的密码插件陷阱

MySQL 8.0 默认启用caching_sha2_password认证插件,而 SpringBoot 2.1.x 默认依赖的mysql-connector-java:8.0.11驱动虽支持,但需显式配置allowPublicKeyRetrieval=true,否则连接时抛Public Key Retrieval is not allowed。更糟的是,若你用CREATE USER语句未指定插件,新用户会继承全局插件,导致 JDBC 连接失败。正确做法是强制降级为兼容性最好的mysql_native_password:

-- 登录 MySQL(用初始化时生成的临时密码) mysql -u root -p -- 执行以下命令(注意:必须分两步!先创建用户,再修改插件) CREATE USER 'springboot_dev'@'%' IDENTIFIED WITH mysql_native_password BY 'P@ssw0rd123'; GRANT ALL PRIVILEGES ON *.* TO 'springboot_dev'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES; -- 验证插件类型(必须返回 mysql_native_password) SELECT user, host, plugin FROM mysql.user WHERE user = 'springboot_dev';

参数说明:WITH mysql_native_password是 MySQL 8.0 新语法,替代旧版IDENTIFIED BY PASSWORD;FLUSH PRIVILEGES不可省略,否则权限不生效;%表示允许任意 IP 连接,生产环境请替换为具体网段如192.168.10.%。

2.3 SpringBoot 工程初始化:精准依赖 + 零配置启动

很多项目失败源于pom.xml依赖冲突。例如同时引入spring-boot-starter-jdbc和mybatis-spring-boot-starter,后者会传递引入HikariCP,而前者默认用HikariCP,看似没问题,但若mybatis版本较老(如 2.1.0),其 HikariCP 版本为 3.2.3,与 SpringBoot 2.7+ 要求的 4.0.3 不兼容,导致DataSource初始化失败。我们源码包中pom.xml严格锁定:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 关键:只引入 JDBC starter,不带任何 ORM --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 显式指定 MySQL 驱动版本,8.0.33 对应驱动 8.0.33 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>8.0.33</version> </dependency> </dependencies>

注意:mysql-connector-java必须设为runtime作用域,否则编译期会触发驱动类加载,导致ClassNotFoundException;version必须与 MySQL 服务端版本严格匹配,8.0.33 服务端用 8.0.33 驱动,不可混用 8.0.28 驱动。


3. application.yml 核心配置:每个参数都是线上翻车现场换来的

3.1 数据源基础配置:URL 字符串里的 7 个生死参数

SpringBoot 的spring.datasource.url看似简单,实则是 JDBC 连接成败的总开关。我们源码包中application.yml的配置经过 3 轮压测验证,关键参数如下:

spring: datasource: url: >- jdbc:mysql://127.0.0.1:3306/testdb? useSSL=false& serverTimezone=Asia/Shanghai& allowPublicKeyRetrieval=true& enabledTLSProtocols=TLSv1.2& characterEncoding=utf8mb4& autoReconnect=true& failOverReadOnly=false username: springboot_dev password: P@ssw0rd123 driver-class-name: com.mysql.cj.jdbc.Driver

参数逐条解析:

  • useSSL=false:MySQL 8.0.28+ 已废弃,但保留向后兼容;若服务端强制 SSL,则必须删掉此项并配置证书路径。
  • serverTimezone=Asia/Shanghai:必须显式设置,否则java.time.LocalDateTime插入数据库时区错乱,表现为时间快 8 小时或慢 8 小时。
  • allowPublicKeyRetrieval=true:仅当服务端用caching_sha2_password时需要,但我们已创建用户时指定mysql_native_password,此项可删,留着是防未来切换认证方式。
  • enabledTLSProtocols=TLSv1.2:MySQL 8.0.28+ 强制要求,否则连接直接拒绝,错误日志无明确提示。
  • characterEncoding=utf8mb4:支持 emoji 和生僻字,utf8在 MySQL 中实际是utf8mb3,不支持四字节 UTF-8。
  • autoReconnect=true:网络抖动时自动重连,但不能替代连接池健康检查,仅作兜底。
  • failOverReadOnly=false:主从架构下防止误连从库变成只读,此处单机部署设为 false 即可。

3.2 HikariCP 连接池调优:为什么 maxPoolSize=20 反而拖垮性能?

HikariCP 是 SpringBoot 默认连接池,但maxPoolSize不是越大越好。某次线上事故:将maxPoolSize从 10 改为 50,QPS 反而下降 40%,原因是 MySQL 服务端max_connections默认 151,50 个连接并发打过去,大量线程阻塞在Waiting for table metadata lock。我们源码包中application.yml的连接池配置经 JMeter 1000 并发压测验证:

spring: datasource: hikari: # 连接池大小:CPU 核数 * 2 + 1 是黄金公式,4 核机器设为 9 maximum-pool-size: 9 # 连接最长存活时间,避免长连接占用服务端资源 max-lifetime: 1800000 # 30 分钟 # 连接空闲超时,回收不用的连接 idle-timeout: 600000 # 10 分钟 # 连接获取超时,避免线程无限等待 connection-timeout: 30000 # 30 秒 # 测试连接有效性 SQL,MySQL 用 SELECT 1 connection-test-query: SELECT 1 # 初始化时创建的连接数,避免首请求延迟 initialization-fail-timeout: 1

逻辑说明:maximum-pool-size=9是基于 4 核 CPU 的保守值,若应用有大量 IO 等待(如调外部 HTTP),可适度提高至 15;max-lifetime=1800000强制连接 30 分钟后重建,规避 MySQL 的wait_timeout(默认 28800 秒=8 小时)导致的连接假死;connection-test-query必须设为SELECT 1,validationQuery在 HikariCP 中已弃用。

3.3 JdbcTemplate 基础用法:从查询到事务的最小可行代码

SpringBoot 自动注入JdbcTemplate,但新手常犯两个错误:一是直接 newJdbcTemplate导致无法使用连接池,二是事务注解@Transactional加在 private 方法上无效。我们源码包中UserDao.java给出最简可用范式:

@Repository public class UserDao { private final JdbcTemplate jdbcTemplate; public UserDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } // 查询单条记录(注意:返回 null 而非异常) public User findById(Long id) { String sql = "SELECT id, name, email FROM users WHERE id = ?"; try { return jdbcTemplate.queryForObject(sql, new Object[]{id}, (rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email"))); } catch (EmptyResultDataAccessException e) { return null; // 明确处理无结果 } } // 批量插入(比单条 insert 快 10 倍以上) public int batchInsert(List<User> users) { String sql = "INSERT INTO users (name, email) VALUES (?, ?)"; return jdbcTemplate.batchUpdate(sql, users.stream().map(u -> new Object[]{u.getName(), u.getEmail()}).collect(Collectors.toList()) ).length; } // 事务方法:必须 public + 类级别代理生效 @Transactional(rollbackFor = Exception.class) public void transferMoney(Long fromId, Long toId, BigDecimal amount) { // 扣减 from 账户 jdbcTemplate.update("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromId); // 增加 to 账户 jdbcTemplate.update("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, toId); // 故意抛异常测试回滚 if (amount.compareTo(new BigDecimal("1000")) > 0) { throw new RuntimeException("Transfer too large"); } } }

参数说明:queryForObject第二个参数new Object[]{id}是 SQL 参数占位符值,第三个参数是RowMapper,将ResultSet映射为User对象;batchUpdate接收二维Object[][]或List<Object[]>,我们用stream().map()构造,避免手动循环;@Transactional的rollbackFor显式指定所有异常都回滚,避免RuntimeException外的异常不触发回滚。


4. 避坑指南:那些让你加班到凌晨三点的 JDBC 翻车现场

4.1 现象:启动时报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

原因:MySQL 5.1+ 驱动类名已从com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver,而 SpringBoot 2.4+ 默认不再自动注册驱动,必须显式配置spring.datasource.driver-class-name。若配置文件中漏写此项,或写错类名,就会触发此异常。
解决:检查application.yml中driver-class-name是否为com.mysql.cj.jdbc.Driver,且mysql-connector-java依赖版本 ≥ 6.0.6(对应 MySQL 5.7+)。

4.2 现象:查询中文字段返回乱码(如李国隌)

原因:三个层面编码不一致:MySQL 服务端character_set_server=utf8mb4、数据库/表字符集utf8mb4_unicode_ci、JDBC URL 中characterEncoding=utf8mb4,三者缺一不可。常见错误是只改了 URL,忘了执行ALTER DATABASE testdb CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;。
解决:登录 MySQL 执行SHOW VARIABLES LIKE 'character_set_%';和SHOW CREATE DATABASE testdb;,确认全部为utf8mb4;再检查 JDBC URL 是否含characterEncoding=utf8mb4。

4.3 现象:LocalDateTime字段插入数据库后时间偏移 8 小时

原因:JVM 时区与 MySQL 时区不一致。JVM 默认用系统时区(如Asia/Shanghai),而 MySQLsystem_time_zone可能是CST(美国中部时间),导致LocalDateTime序列化时被错误转换。
解决:在 JDBC URL 中强制指定serverTimezone=Asia/Shanghai,并在 MySQL 中执行SET GLOBAL time_zone = '+8:00';,然后重启 MySQL 服务。

4.4 现象:JdbcTemplate.update()返回 0,但数据库实际已更新

原因:SQL 中WHERE条件不匹配任何记录,update()方法返回影响行数,0 表示无记录被更新,不是异常。新手误以为是连接失败,疯狂查网络和权限。
解决:增加日志输出int rows = jdbcTemplate.update(sql, params); log.info("Updated {} rows", rows);,明确区分“更新成功 0 行”和“执行失败”。

4.5 现象:应用启动后首次查询极慢(>5 秒),后续正常

原因:HikariCP 默认initialization-fail-timeout=-1,表示不初始化连接,首次getConnection()时才真正建立物理连接,而 MySQL 驱动首次加载需解析大量元数据。
解决:在application.yml中添加spring.datasource.hikari.initialization-fail-timeout=1,让连接池启动时就校验连接,把耗时前置到启动阶段。


5. 生产级验证:用 3 个 Shell 脚本完成全链路冒烟测试

5.1 数据库连通性自检脚本:check-mysql.sh

线上发布前,必须验证 MySQL 服务可达、用户权限正确、字符集合规。我们提供check-mysql.sh,无需 Java 环境,纯 Bash 调用mysql客户端:

#!/bin/bash # 检查 MySQL 连通性、用户权限、字符集 HOST="127.0.0.1" PORT="3306" USER="springboot_dev" PASS="P@ssw0rd123" DB="testdb" echo "=== Step 1: Ping MySQL service ===" if ! nc -z $HOST $PORT; then echo "FAIL: MySQL port $PORT not reachable" exit 1 fi echo "=== Step 2: Login and check user privileges ===" if ! mysql -h$HOST -P$PORT -u$USER -p$PASS -e "SELECT 1;" >/dev/null 2>&1; then echo "FAIL: Cannot login with user $USER" exit 1 fi echo "=== Step 3: Verify character set ===" CHARSET=$(mysql -h$HOST -P$PORT -u$USER -p$PASS -Nse "SELECT @@character_set_database;") if [ "$CHARSET" != "utf8mb4" ]; then echo "FAIL: Database charset is $CHARSET, expected utf8mb4" exit 1 fi echo "PASS: All checks passed"

使用说明:chmod +x check-mysql.sh && ./check-mysql.sh;脚本通过nc检查端口、mysql命令登录、SQL 查询变量三重验证,覆盖网络、认证、配置三类故障。

5.2 SpringBoot 应用健康检查脚本:check-app.sh

SpringBoot Actuator 的/actuator/health端点返回 JSON,但默认不包含数据库详情。我们源码包中已启用show-details=always,此脚本解析 JSON 判断数据库是否 UP:

#!/bin/bash # 检查 SpringBoot 应用健康状态,重点验证 db 状态 APP_URL="http://localhost:8080/actuator/health" echo "=== Checking application health ===" HEALTH_JSON=$(curl -s "$APP_URL") # 检查整体状态 STATUS=$(echo $HEALTH_JSON | jq -r '.status') if [ "$STATUS" != "UP" ]; then echo "FAIL: Application status is $STATUS" exit 1 fi # 检查数据库状态(需 actuator 配置 management.endpoint.health.show-details=always) DB_STATUS=$(echo $HEALTH_JSON | jq -r '.components.db.status') if [ "$DB_STATUS" != "UP" ]; then echo "FAIL: Database component status is $DB_STATUS" # 输出详细错误 ERROR_MSG=$(echo $HEALTH_JSON | jq -r '.components.db.details.error // "unknown"') echo "Error detail: $ERROR_MSG" exit 1 fi echo "PASS: Application and database are UP"

依赖说明:需提前安装jq(JSON 解析工具),apt install jq或brew install jq;脚本用curl获取健康端点,用jq提取.components.db.status字段,确保数据库探针真实生效。

5.3 全链路冒烟测试脚本:smoke-test.sh

模拟真实业务场景:创建用户 → 查询用户 → 更新用户 → 删除用户。此脚本调用 SpringBoot 的 REST API,验证 JDBC 操作全流程:

#!/bin/bash # 全链路冒烟测试:CRUD 用户 API_BASE="http://localhost:8080/api/users" echo "=== Smoke Test: Create User ===" CREATE_RESP=$(curl -s -X POST "$API_BASE" \ -H "Content-Type: application/json" \ -d '{"name":"SmokeTestUser","email":"smoke@test.com"}') USER_ID=$(echo $CREATE_RESP | jq -r '.id') if [ "$USER_ID" = "null" ] || [ -z "$USER_ID" ]; then echo "FAIL: Create user failed" exit 1 fi echo "=== Smoke Test: Get User by ID ===" GET_RESP=$(curl -s "$API_BASE/$USER_ID") NAME=$(echo $GET_RESP | jq -r '.name') if [ "$NAME" != "SmokeTestUser" ]; then echo "FAIL: Get user returned wrong name: $NAME" exit 1 fi echo "=== Smoke Test: Update User ===" curl -s -X PUT "$API_BASE/$USER_ID" \ -H "Content-Type: application/json" \ -d '{"name":"UpdatedSmoke","email":"updated@test.com"}' >/dev/null UPDATED_NAME=$(curl -s "$API_BASE/$USER_ID" | jq -r '.name') if [ "$UPDATED_NAME" != "UpdatedSmoke" ]; then echo "FAIL: Update user failed" exit 1 fi echo "=== Smoke Test: Delete User ===" curl -s -X DELETE "$API_BASE/$USER_ID" >/dev/null DELETE_CHECK=$(curl -s "$API_BASE/$USER_ID" | jq -r '.error // "not found"') if [ "$DELETE_CHECK" != "not found" ]; then echo "FAIL: Delete user failed" exit 1 fi echo "PASS: All smoke tests passed"

技术要点:脚本用curl模拟 HTTP 请求,jq解析响应 JSON;每步都做断言,失败立即退出;USER_ID从创建响应中提取,确保测试数据隔离;删除后再次 GET 验证是否真删除,避免假成功。


6. 进阶技巧:用 Docker Compose 一键拉起 MySQL + SpringBoot 开发环境

6.1 为什么不用纯 Docker?因为卷挂载和时区是玄学

Docker 官方 MySQL 镜像默认时区是UTC,而中国开发者需要Asia/Shanghai。若只在docker run中加-e TZ=Asia/Shanghai,MySQL 容器内date命令显示正确,但 JDBC 连接时serverTimezone参数仍需显式设置,否则LocalDateTime依然错乱。更糟的是,docker volume挂载宿主机目录时,Linux 权限映射可能让 MySQL 无法写入data目录。我们提供的docker-compose.yml用三重保险解决:

version: '3.8' services: mysql: image: mysql:8.0.33 container_name: mysql-dev environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: testdb MYSQL_USER: springboot_dev MYSQL_PASSWORD: P@ssw0rd123 # 关键:强制容器时区为中国标准时间 TZ: Asia/Shanghai # 关键:用 tmpfs 挂载,避免权限问题,且重启不丢失 tmpfs: - /var/lib/mysql # 关键:覆盖默认启动命令,显式指定时区和字符集 command: > --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --time-zone='+08:00' ports: - "3306:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "springboot_dev", "-pP@ssw0rd123"] timeout: 20s retries: 10 app: build: . container_name: springboot-app depends_on: mysql: condition: service_healthy environment: # 关键:JDBC URL 中 serverTimezone 必须与容器时区一致 SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/testdb?serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4&allowPublicKeyRetrieval=true SPRING_DATASOURCE_USERNAME: springboot_dev SPRING_DATASOURCE_PASSWORD: P@ssw0rd123 ports: - "8080:8080" # 关键:应用容器也设时区,保持 JVM 与 MySQL 一致 environment: - TZ=Asia/Shanghai

参数说明:tmpfs将 MySQL 数据目录挂载到内存,避免宿主机权限问题,且开发环境无需持久化;command覆盖默认启动参数,--time-zone='+08:00'比TZ环境变量更底层;healthcheck用mysqladmin ping真实检测数据库就绪,而非只看端口;depends_on的condition: service_healthy确保 SpringBoot 容器只在 MySQL 健康后启动。

6.2 Dockerfile 构建优化:跳过测试 + 分层缓存

SpringBoot Maven 构建默认执行所有测试,而开发环境无需运行单元测试,可跳过加速构建。我们Dockerfile采用多阶段构建,利用 Maven 本地仓库分层缓存:

# 构建阶段:只下载依赖,缓存复用率高 FROM maven:3.8.6-openjdk-17 AS builder WORKDIR /app COPY pom.xml . # 先下载依赖,利用 Docker 层缓存 RUN mvn dependency:go-offline -B COPY src ./src # 跳过测试,仅编译打包 RUN mvn clean package -Dmaven.test.skip=true -B # 运行阶段:精简镜像 FROM openjdk:17-jre-slim WORKDIR /app # 复制构建产物,不带 Maven 和源码 COPY --from=builder /app/target/*.jar app.jar # 暴露端口 EXPOSE 8080 # 启动命令 ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","app.jar"]

优化点:mvn dependency:go-offline单独一层,只要pom.xml不变,该层永远命中缓存;-Dmaven.test.skip=true跳过测试编译和执行,构建时间减少 60%;openjdk:17-jre-slim镜像仅 120MB,比openjdk:17小 300MB;-Djava.security.egd=file:/dev/./urandom解决容器内SecureRandom初始化慢的问题。

6.3 本地开发调试技巧:IntelliJ IDEA 远程 Debug MySQL 容器

当 JDBC 报错难以定位时,需进入 MySQL 容器内部抓包。我们提供一键进入容器并启用 MySQL 通用日志的方法:

# 1. 进入 MySQL 容器 docker exec -it mysql-dev bash # 2. 启用通用日志(记录所有 SQL) mysql -u root -proot123 -e "SET GLOBAL general_log = 'ON'; SET GLOBAL log_output = 'TABLE';" # 3. 查看最近 10 条 SQL(在容器内执行) mysql -u root -proot123 -e "SELECT argument FROM mysql.general_log ORDER BY event_time DESC LIMIT 10;" # 4. 关闭日志(避免磁盘爆满) mysql -u root -proot123 -e "SET GLOBAL general_log = 'OFF';"

实战经验:通用日志默认输出到文件,但容器内文件系统易满,故设为TABLE;mysql.general_log表在mysql库中,需用root用户查询;日志包含客户端 IP,可确认 SpringBoot 容器是否真的连到了这个 MySQL 实例。

从那以后我每次新建 SpringBoot 项目,都强制走一遍check-mysql.sh→docker-compose up -d→smoke-test.sh三步验证,哪怕只是改了个 Controller 路径。因为 JDBC 这层抽象太薄,薄到一个字符集参数就能让整个服务返回乱码,而这种问题永远在上线前最后一刻才爆发。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进