1. SpringBoot与MySQL本地开发环境搭建实战
刚接触SpringBoot的开发者常遇到的第一个实操需求就是连接本地MySQL数据库。这看似基础的操作实则暗藏不少技术细节,从环境配置到框架整合,每一步都可能成为新手路上的绊脚石。作为经历过无数"Could not create connection"报错的老手,今天我就带大家完整走通这个流程,重点分享那些官方文档不会告诉你的实战经验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与工具选型
2.1 开发环境清单
- JDK 1.8+:推荐Amazon Corretto 11(长期支持版本)
- IDE:IntelliJ IDEA Ultimate(社区版也够用)
- MySQL版本:8.0.28(注意5.7与8.0的认证协议差异)
- SpringBoot版本:2.7.3(与3.x的配置差异会特别说明)
重要提示:MySQL 8.x默认使用caching_sha2_password认证插件,而旧版驱动可能不支持,建议在安装时选择"Legacy Authentication"或修改mysql.user表插件类型。
2.2 依赖配置关键点
在pom.xml中需要重点关注以下依赖:
xml复制<dependencies>
<!-- SpringBoot Starter JDBC(包含HikariCP连接池) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
<scope>runtime</scope>
</dependency>
<!-- 如需使用MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
</dependencies>
3. 数据库连接配置详解
3.1 application.yml最佳实践
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/your_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
参数解析:
useSSL=false:本地开发可禁用SSL加密allowPublicKeyRetrieval=true:解决MySQL 8.x的Public Key Retrieval报错serverTimezone:必须设置,避免时区问题导致的时间字段异常
3.2 常见连接问题排查
-
Access denied for user:
- 检查MySQL用户是否有远程连接权限(本地连接也需要)
- 执行:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY 'password';
-
Public Key Retrieval is not allowed:
- 在连接字符串添加
allowPublicKeyRetrieval=true - 或修改MySQL用户密码插件:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
- 在连接字符串添加
-
时区问题:
- 报错:The server time zone value 'xxx' is unrecognized
- 解决方案:连接字符串添加
serverTimezone=Asia/Shanghai
4. MyBatis整合实战技巧
4.1 基础配置
yaml复制mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4.2 动态SQL避坑指南
在mapper.xml中处理特殊符号:
xml复制<!-- 错误示范 -->
<select id="findUsers" resultType="User">
SELECT * FROM user WHERE age < 30
</select>
<!-- 正确写法 -->
<select id="findUsers" resultType="User">
SELECT * FROM user WHERE age < 30
</select>
需要转义的符号对照表:
| 符号 | XML转义 | 说明 |
|---|---|---|
| < | < | 小于 |
| > | > | 大于 |
| & | & | 和号 |
| " | " | 双引号 |
| ' | ' | 单引号 |
4.3 批量操作性能优化
java复制@Mapper
public interface UserMapper {
// 批量插入(注意foreach语法)
@Insert("<script>" +
"INSERT INTO user(name,age) VALUES " +
"<foreach collection='list' item='item' separator=','>" +
"(#{item.name},#{item.age})" +
"</foreach>" +
"</script>")
int batchInsert(@Param("list") List<User> users);
}
5. 高级特性与性能调优
5.1 连接池监控
SpringBoot默认使用HikariCP,可通过以下配置开启监控:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
访问/actuator/metrics/hikaricp.connections获取实时连接池状态。
5.2 多数据源配置
当需要连接多个MySQL实例时:
java复制@Configuration
public class DataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}
对应yml配置:
yaml复制spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
secondary:
url: jdbc:mysql://localhost:3306/db2
username: root
password: 123456
6. 常见问题解决方案速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Communications link failure | MySQL服务未启动 | sudo service mysql start |
| Unknown database 'xxx' | 数据库不存在 | 先创建数据库:CREATE DATABASE xxx; |
| Table 'xxx' doesn't exist | 表不存在 | 检查表名或执行建表SQL |
| Could not get JDBC Connection | 连接池耗尽 | 增加maximum-pool-size或检查连接泄漏 |
| Parameter index out of range | MyBatis参数不匹配 | 检查#{param}与@Param注解是否一致 |
7. 开发调试技巧
- SQL日志打印:
yaml复制logging:
level:
org.springframework.jdbc.core.JdbcTemplate: DEBUG
org.mybatis.spring.SqlSessionUtils: DEBUG
- 快速测试连接:
java复制@SpringBootTest
class DbConnectionTest {
@Autowired
private DataSource dataSource;
@Test
void testConnection() throws SQLException {
try(Connection conn = dataSource.getConnection()) {
DatabaseMetaData meta = conn.getMetaData();
System.out.println("连接成功:" + meta.getDatabaseProductName());
}
}
}
- 使用Arthas监控MyBatis SQL:
bash复制# 启动Arthas后执行
watch org.apache.ibatis.session.defaults.DefaultSqlSession selectOne '{params,returnObj}' -x 3
8. 安全加固建议
- 生产环境必须启用SSL:
yaml复制url: jdbc:mysql://localhost:3306/db?useSSL=true&requireSSL=true&verifyServerCertificate=true
- 使用加密密码:
yaml复制spring:
datasource:
password: ${DB_PASSWORD} # 从环境变量读取
- 最小权限原则:
sql复制CREATE USER 'app_user'@'%' IDENTIFIED BY 'complex_password';
GRANT SELECT, INSERT, UPDATE ON db.* TO 'app_user'@'%';
9. 版本升级注意事项
从SpringBoot 2.x升级到3.x需要关注:
- MySQL驱动包名变更:
xml复制<!-- 旧版 -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- 新版 -->
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
- 默认的SSL验证更严格,测试环境可能需要添加:
yaml复制url: jdbc:mysql://localhost:3306/db?sslMode=DISABLED
- MyBatis Starter配置项变化:
yaml复制# 旧版
mybatis.configuration.map-underscore-to-camel-case=true
# 新版
mybatis.configuration.map-underscore-to-camel-case: true
10. 性能优化实战
-
连接池参数调优公式:
- 线程数 = (核心数 * 2) + 有效磁盘数
- 示例:4核CPU + 1块SSD → (4*2)+1 = 9
yaml复制hikari: maximum-pool-size: 9 minimum-idle: 3 -
批量插入优化对比:
| 方式 | 1万条耗时 | 备注 |
|---|---|---|
| 单条循环 | 12.8s | 绝对禁止 |
| Batch模式 | 1.2s | 需要rewriteBatchedStatements=true |
| 多值INSERT | 0.4s | 如VALUES(...),(...),(...) |
| Load Data | 0.1s | 适合大数据量导入 |
- 索引使用检查:
java复制@Repository
public class UserRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public void explainQuery(Long userId) {
String sql = "EXPLAIN SELECT * FROM user WHERE id = ?";
Map<String, Object> result = jdbcTemplate.queryForMap(sql, userId);
System.out.println("执行计划:" + result);
}
}
11. 扩展应用场景
- 多租户方案:
java复制public class TenantDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TenantContext.getCurrentTenant();
}
}
- 分库分表示例:
yaml复制spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
url: jdbc:mysql://localhost:3306/db0
ds1:
url: jdbc:mysql://localhost:3306/db1
sharding:
tables:
user:
actual-data-nodes: ds$->{0..1}.user_$->{0..15}
- 存储过程调用:
java复制@Autowired
private JdbcTemplate jdbcTemplate;
public void callProcedure() {
jdbcTemplate.call(conn -> {
CallableStatement cs = conn.prepareCall("{call proc_update_stats(?)}");
cs.setString(1, "daily");
return cs;
}, Collections.emptyList());
}
12. 监控与报警
- Prometheus监控配置:
yaml复制management:
metrics:
export:
prometheus:
enabled: true
endpoint:
prometheus:
enabled: true
-
关键监控指标:
hikaricp_connections_active:活跃连接数hikaricp_connections_idle:空闲连接数jdbc_connections_max:最大连接数jdbc_connections_min:最小连接数
-
自定义健康检查:
java复制@Component
public class DbHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try (Connection conn = dataSource.getConnection()) {
if (conn.isValid(1000)) {
return Health.up().build();
}
return Health.down().build();
} catch (SQLException e) {
return Health.down(e).build();
}
}
}
13. 事务管理深度解析
- 声明式事务边界控制:
java复制@Service
public class UserService {
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = Exception.class
)
public void createUser(User user) {
// 业务逻辑
}
}
- 事务传播行为对比:
| 传播类型 | 说明 | 适用场景 |
|---|---|---|
| REQUIRED | 支持当前事务,不存在则新建 | 默认选择 |
| REQUIRES_NEW | 新建事务,挂起当前事务 | 独立日志操作 |
| NESTED | 嵌套事务,外层回滚会连带内层 | 复杂业务分步处理 |
| SUPPORTS | 支持当前事务,不存在则以非事务执行 | 查询方法 |
- 事务失效的7种常见情况:
- 方法非public修饰
- 自调用(this.method())
- 异常被catch未抛出
- 数据库引擎不支持(如MyISAM)
- 传播属性配置为NOT_SUPPORTED
- 多数据源未指定事务管理器
- 方法final或static修饰
14. 数据迁移方案
- 使用Flyway管理迁移脚本:
yaml复制spring:
flyway:
locations: classpath:db/migration
baseline-on-migrate: true
脚本命名规范:V1__Create_user_table.sql
- 大数据量迁移方案对比:
| 工具 | 适用场景 | 特点 |
|---|---|---|
| mysqldump | <10GB | 简单但锁表 |
| MySQL Shell | >10GB | 并行导出导入 |
| DataX | 异构迁移 | 支持断点续传 |
| Canal | 实时同步 | 基于binlog解析 |
- 表结构对比工具推荐:
sql复制-- 生成差异SQL
SELECT CONCAT('ALTER TABLE ', table_name, ' ADD COLUMN ',
column_name, ' ', column_type,
IF(is_nullable='NO', ' NOT NULL', ''),
IF(column_default IS NOT NULL, CONCAT(' DEFAULT ', column_default), ''),
' COMMENT ''', column_comment, ''';') AS ddl
FROM information_schema.columns
WHERE table_schema = 'your_db'
AND table_name = 'your_table'
AND column_name NOT IN (
SELECT column_name
FROM information_schema.columns
WHERE table_schema = 'target_db'
AND table_name = 'target_table'
);
15. 历史数据处理策略
- 归档方案设计:
sql复制-- 创建归档表(结构与原表相同)
CREATE TABLE user_history LIKE user;
-- 添加归档时间字段
ALTER TABLE user_history ADD COLUMN archive_time DATETIME DEFAULT CURRENT_TIMESTAMP;
-- 归档数据(每月执行)
INSERT INTO user_history
SELECT *, NOW() FROM user
WHERE create_time < DATE_SUB(CURRENT_DATE(), INTERVAL 6 MONTH);
-- 删除已归档数据
DELETE FROM user
WHERE create_time < DATE_SUB(CURRENT_DATE(), INTERVAL 6 MONTH);
- 分区表示例:
sql复制CREATE TABLE log_data (
id BIGINT,
log_time DATETIME,
content TEXT,
PRIMARY KEY (id, log_time)
) PARTITION BY RANGE (YEAR(log_time)) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
- 数据清理定时任务:
java复制@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
@Transactional
public void cleanOldData() {
LocalDateTime threshold = LocalDateTime.now().minusMonths(6);
jdbcTemplate.update("DELETE FROM user WHERE create_time < ?", threshold);
}
16. 复杂查询优化案例
- 分页查询优化:
sql复制-- 低效写法(偏移量大时性能差)
SELECT * FROM user ORDER BY id LIMIT 1000000, 10;
-- 优化方案1:基于主键分页
SELECT * FROM user WHERE id > 1000000 ORDER BY id LIMIT 10;
-- 优化方案2:延迟关联
SELECT t.* FROM user t
JOIN (SELECT id FROM user ORDER BY id LIMIT 1000000, 10) tmp
ON t.id = tmp.id;
- 大数据量导出方案:
java复制public void exportUsers(OutputStream output) throws SQLException {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement(
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery("SELECT * FROM user")) {
conn.setAutoCommit(false);
stmt.setFetchSize(1000); // 分批获取
CSVPrinter printer = new CSVPrinter(
new OutputStreamWriter(output),
CSVFormat.DEFAULT);
ResultSetMetaData meta = rs.getMetaData();
int colCount = meta.getColumnCount();
// 写入表头
List<String> headers = new ArrayList<>();
for (int i = 1; i <= colCount; i++) {
headers.add(meta.getColumnName(i));
}
printer.printRecord(headers);
// 分批写入数据
while (rs.next()) {
List<Object> values = new ArrayList<>();
for (int i = 1; i <= colCount; i++) {
values.add(rs.getObject(i));
}
printer.printRecord(values);
}
}
}
- 全文索引使用:
sql复制-- 创建全文索引
ALTER TABLE article ADD FULLTEXT INDEX ft_index (title, content) WITH PARSER ngram;
-- 使用MATCH查询
SELECT * FROM article
WHERE MATCH(title, content) AGAINST('关键词' IN BOOLEAN MODE);
17. 高可用架构设计
- 主从复制配置:
ini复制# my.cnf主库配置
[mysqld]
server-id=1
log-bin=mysql-bin
binlog-format=ROW
# my.cnf从库配置
[mysqld]
server-id=2
relay-log=mysql-relay-bin
read-only=1
- SpringBoot多数据源路由:
java复制public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<Boolean> readOnly = new ThreadLocal<>();
public static void setReadOnly(boolean flag) {
readOnly.set(flag);
}
@Override
protected Object determineCurrentLookupKey() {
return readOnly.get() != null && readOnly.get() ? "read" : "write";
}
public static void clear() {
readOnly.remove();
}
}
// 使用AOP自动设置
@Around("@annotation(readOnly)")
public Object routeReadOnly(ProceedingJoinPoint jp, ReadOnly readOnly) throws Throwable {
try {
ReplicationRoutingDataSource.setReadOnly(true);
return jp.proceed();
} finally {
ReplicationRoutingDataSource.clear();
}
}
- 故障转移策略:
yaml复制spring:
datasource:
url: jdbc:mysql:replication://master:3306,slave1:3306,slave2:3306/db
username: user
password: pass
hikari:
read-only: false
driver-class-name: com.mysql.cj.jdbc.Driver
tomcat:
validation-query: SELECT 1
test-on-borrow: true
test-while-idle: true
time-between-eviction-runs-millis: 30000
18. 数据安全实践
- 敏感数据加密:
java复制@ColumnTransformer(
read = "AES_DECRYPT(UNHEX(credit_card), 'encryption_key')",
write = "HEX(AES_ENCRYPT(?, 'encryption_key'))"
)
@Column(name = "credit_card")
private String creditCard;
- 审计日志方案:
java复制@EntityListeners(AuditingEntityListener.class)
@Entity
public class User {
@CreatedBy
private String createdBy;
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedBy
private String modifiedBy;
@LastModifiedDate
private LocalDateTime modifiedDate;
}
- 数据脱敏处理:
java复制public class DataMaskingUtil {
public static String maskPhone(String phone) {
if (StringUtils.isEmpty(phone)) return "";
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
public static String maskIdCard(String idCard) {
if (StringUtils.isEmpty(idCard)) return "";
return idCard.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1**********$2");
}
}
19. 性能测试与基准
-
JMeter压测配置要点:
- 线程组:100并发,持续5分钟
- JDBC连接配置:与生产环境一致
- 测试SQL:混合读写(70%读,30%写)
- 监控指标:TPS、响应时间、错误率
-
性能基准参考(AWS RDS MySQL 8.0):
| 规格 | QPS | 平均延迟 | 最大连接数 |
|---|---|---|---|
| db.t3.micro | 1,200 | 15ms | 45 |
| db.m5.large | 8,500 | 8ms | 600 |
| db.r5.xlarge | 15,000 | 5ms | 1,200 |
- 慢查询分析方法:
sql复制-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; # 超过1秒记录
SET GLOBAL log_queries_not_using_indexes = 'ON';
-- 分析日志
mysqldumpslow -s t /var/log/mysql/mysql-slow.log
20. 云原生实践
- Kubernetes部署MySQL:
yaml复制# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "securepassword"
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-persistent-storage
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
- SpringBoot连接配置:
yaml复制spring:
datasource:
url: jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT}/db
username: root
password: ${DB_PASSWORD}
hikari:
connection-timeout: 3000
initialization-fail-timeout: 0
- 健康检查端点:
yaml复制management:
health:
db:
enabled: true
diskspace:
enabled: true
readinessState:
enabled: true
