@llryiop/avatar-boot-cli 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/exam-question-generate-api.md +163 -0
- package/package.json +1 -1
- package/src/prompts.js +3 -3
- package/src/transform.js +1 -1
- package/templates/.claude/skills/avatar-boot-starter-feign/README.md +243 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/SKILL.md +47 -219
- package/templates/.claude/skills/avatar-boot-starter-feign/references//345/212/237/350/203/275/350/257/246/350/247/243.md +65 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/references//345/277/253/351/200/237/346/216/245/345/205/245/346/214/207/345/215/227.md +75 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/references//351/205/215/347/275/256/345/217/202/350/200/203.md +70 -0
- package/templates/.claude/skills/avatar-boot-starter-job/README.md +437 -0
- package/templates/.claude/skills/avatar-boot-starter-job/SKILL.md +35 -414
- package/templates/.claude/skills/avatar-boot-starter-job/references//345/270/270/350/247/201/351/227/256/351/242/230.md +55 -0
- package/templates/.claude/skills/avatar-boot-starter-job/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +124 -0
- package/templates/.claude/skills/avatar-boot-starter-job/references//347/233/221/346/216/247/346/214/207/346/240/207.md +72 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/README.md +580 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/SKILL.md +36 -560
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//346/234/200/344/275/263/345/256/236/350/267/265.md +43 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//346/240/270/345/277/203/345/212/237/350/203/275.md +117 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//351/205/215/347/275/256/345/217/202/350/200/203.md +54 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/README.md +572 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/SKILL.md +40 -550
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//345/256/236/344/275/223/344/270/216/345/212/237/350/203/275.md +96 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/346/225/260/346/215/256/346/272/220.md +91 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//351/253/230/347/272/247/347/211/271/346/200/247/344/270/216/351/205/215/347/275/256.md +59 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/README.md +901 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/SKILL.md +40 -879
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//345/212/237/350/203/275/344/275/277/347/224/250.md +134 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +96 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//346/225/205/351/232/234/346/216/222/346/237/245.md +64 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/README.md +594 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/SKILL.md +52 -570
- package/templates/.claude/skills/avatar-boot-starter-oss/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +77 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/references//346/240/270/345/277/203/345/212/237/350/203/275.md +94 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/references//350/247/204/350/214/203/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +61 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/README.md +586 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/SKILL.md +42 -566
- package/templates/.claude/skills/avatar-boot-starter-redis/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +78 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/references//346/225/260/346/215/256/346/223/215/344/275/234.md +111 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/references//351/253/230/347/272/247/345/212/237/350/203/275.md +90 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/README.md +662 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/SKILL.md +48 -640
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//346/240/270/345/277/203/345/212/237/350/203/275.md +101 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//351/205/215/347/275/256/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +44 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//351/253/230/347/272/247/347/211/271/346/200/247.md +71 -0
- package/templates/.claude/skills/avatar-boot-starter-web/README.md +1007 -0
- package/templates/.claude/skills/avatar-boot-starter-web/SKILL.md +150 -1003
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-LogInfo/346/263/250/350/247/243.md +75 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/345/205/250/345/261/200/345/274/202/345/270/270/345/244/204/347/220/206.md +90 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/346/214/207/346/240/207/347/233/221/346/216/247.md +74 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/346/227/245/345/277/227/344/275/223/347/263/273.md +73 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/350/257/267/346/261/202/344/270/212/344/270/213/346/226/207.md +77 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/277/253/351/200/237/346/216/245/345/205/245/346/214/207/345/215/227.md +52 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//346/263/250/346/204/217/344/272/213/351/241/271.md +68 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//350/207/252/345/256/232/344/271/211/346/211/251/345/261/225/346/214/207/345/215/227.md +107 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//351/205/215/347/275/256/345/217/202/350/200/203.md +107 -0
- package/templates/.claude/skills/crud-generator/SKILL.md +133 -64
- package/templates/.claude/skills/database-design/README.md +207 -0
- package/templates/.claude/skills/database-design/SKILL.md +469 -82
- package/templates/.claude/skills/database-design/references//345/221/275/345/220/215/350/247/204/350/214/203.md +232 -0
- package/templates/.claude/skills/database-design/references//345/255/227/346/256/265/347/261/273/345/236/213/350/247/204/350/214/203.md +400 -0
- package/templates/.claude/skills/database-design/references//347/264/242/345/274/225/350/247/204/350/214/203.md +506 -0
- package/templates/README.md +65 -100
- package/templates/avatar-scaffold-api/pom.xml +0 -5
- package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/api/LoginFeignClient.java +2 -0
- package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/exception/LoginErrorCode.java +25 -0
- package/templates/avatar-scaffold-service/pom.xml +25 -87
- package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/feign/DemoFeign.java +4 -1
- package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/repository/UserLoginRepository.java +10 -0
- package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/repository/mapper/UserLoginMapper.java +4 -1
- package/templates/avatar-scaffold-service/src/main/resources/application-dev.yaml +3 -5
- package/templates/avatar-scaffold-service/src/main/resources/application-local.yaml +21 -21
- package/templates/pom.xml +9 -18
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: avatar-boot-starter-mysql
|
|
3
|
+
description: 当涉及 MySQL、数据库、Druid、数据源、MyBatis Plus、SQL 相关功能时使用此技能 - 为 Spring Boot 3.5.3 应用提供基于 Druid 1.2.24 连接池和 MyBatis Plus 3.5.15 的数据库访问能力,包含自动配置、SQL 监控、多数据源支持。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Avatar Boot 的 MySQL 数据库集成模块,基于 Druid 1.2.24 连接池和 MyBatis Plus 3.5.15 提供开箱即用的数据库访问能力。
|
|
7
|
+
|
|
8
|
+
## 功能特性
|
|
9
|
+
|
|
10
|
+
- ✅ **Druid 连接池** - 内置 Druid 1.2.24,提供高性能连接池管理和 SQL 监控
|
|
11
|
+
- ✅ **MyBatis Plus 集成** - 基于 mybatis-plus-spring-boot3-starter 3.5.15,提供增强 CRUD 能力
|
|
12
|
+
- ✅ **SQL 监控** - Druid 内置 SQL 监控面板,实时查看慢 SQL 和连接池状态
|
|
13
|
+
- ✅ **自动分页** - 内置分页插件,支持多种数据库方言
|
|
14
|
+
- ✅ **代码生成** - 支持 MyBatis Plus 代码生成器,快速生成 Entity/Mapper/Service
|
|
15
|
+
- ✅ **多数据源** - 支持多数据源配置和动态切换
|
|
16
|
+
|
|
17
|
+
## 快速开始
|
|
18
|
+
|
|
19
|
+
### 1. 添加依赖
|
|
20
|
+
|
|
21
|
+
在项目的 `pom.xml` 中添加依赖:
|
|
22
|
+
|
|
23
|
+
```xml
|
|
24
|
+
<dependency>
|
|
25
|
+
<groupId>com.iflytek.avatar.boot</groupId>
|
|
26
|
+
<artifactId>avatar-boot-starter-mysql</artifactId>
|
|
27
|
+
</dependency>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
> 版本由 Avatar Boot BOM 统一管理,无需指定 version。
|
|
31
|
+
|
|
32
|
+
### 2. 配置文件
|
|
33
|
+
|
|
34
|
+
在 `application.yml` 中配置数据源:
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
spring:
|
|
38
|
+
datasource:
|
|
39
|
+
type: com.alibaba.druid.pool.DruidDataSource
|
|
40
|
+
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
41
|
+
url: jdbc:mysql://localhost:3306/your_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
|
42
|
+
username: root
|
|
43
|
+
password: your-password
|
|
44
|
+
druid:
|
|
45
|
+
# 连接池配置
|
|
46
|
+
initial-size: 5 # 初始化连接数
|
|
47
|
+
min-idle: 5 # 最小空闲连接数
|
|
48
|
+
max-active: 20 # 最大活跃连接数
|
|
49
|
+
max-wait: 60000 # 获取连接最大等待时间(毫秒)
|
|
50
|
+
# 连接检测
|
|
51
|
+
validation-query: SELECT 1
|
|
52
|
+
test-while-idle: true
|
|
53
|
+
test-on-borrow: false
|
|
54
|
+
test-on-return: false
|
|
55
|
+
time-between-eviction-runs-millis: 60000 # 检测间隔
|
|
56
|
+
min-evictable-idle-time-millis: 300000 # 最小空闲时间
|
|
57
|
+
# 监控统计
|
|
58
|
+
filters: stat,wall,slf4j # 启用统计、防火墙、日志过滤器
|
|
59
|
+
stat-view-servlet:
|
|
60
|
+
enabled: true
|
|
61
|
+
url-pattern: /druid/*
|
|
62
|
+
login-username: admin
|
|
63
|
+
login-password: admin123
|
|
64
|
+
allow: ""
|
|
65
|
+
web-stat-filter:
|
|
66
|
+
enabled: true
|
|
67
|
+
url-pattern: /*
|
|
68
|
+
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
|
|
69
|
+
filter:
|
|
70
|
+
stat:
|
|
71
|
+
slow-sql-millis: 2000 # 慢 SQL 阈值(毫秒)
|
|
72
|
+
log-slow-sql: true # 记录慢 SQL
|
|
73
|
+
merge-sql: true # 合并 SQL 统计
|
|
74
|
+
wall:
|
|
75
|
+
config:
|
|
76
|
+
multi-statement-allow: false # 禁止多语句执行
|
|
77
|
+
delete-allow: true
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. MyBatis Plus 配置
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
mybatis-plus:
|
|
84
|
+
mapper-locations: classpath*:mapper/**/*.xml # Mapper XML 文件位置
|
|
85
|
+
type-aliases-package: com.example.entity # 实体类包路径
|
|
86
|
+
configuration:
|
|
87
|
+
map-underscore-to-camel-case: true # 驼峰命名映射
|
|
88
|
+
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl # SQL 日志
|
|
89
|
+
global-config:
|
|
90
|
+
db-config:
|
|
91
|
+
id-type: assign_id # 主键策略:雪花算法
|
|
92
|
+
logic-delete-field: deleted # 逻辑删除字段
|
|
93
|
+
logic-delete-value: 1 # 已删除值
|
|
94
|
+
logic-not-delete-value: 0 # 未删除值
|
|
95
|
+
table-prefix: t_ # 表名前缀(可选)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Entity / Mapper / Service 模式
|
|
99
|
+
|
|
100
|
+
### Entity 实体类
|
|
101
|
+
|
|
102
|
+
```java
|
|
103
|
+
package com.example.entity;
|
|
104
|
+
|
|
105
|
+
import com.baomidou.mybatisplus.annotation.*;
|
|
106
|
+
import lombok.Data;
|
|
107
|
+
|
|
108
|
+
import java.io.Serial;
|
|
109
|
+
import java.io.Serializable;
|
|
110
|
+
import java.time.LocalDateTime;
|
|
111
|
+
|
|
112
|
+
@Data
|
|
113
|
+
@TableName("t_user")
|
|
114
|
+
public class User implements Serializable {
|
|
115
|
+
|
|
116
|
+
@Serial
|
|
117
|
+
private static final long serialVersionUID = 1L;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 主键(雪花算法)
|
|
121
|
+
*/
|
|
122
|
+
@TableId(type = IdType.ASSIGN_ID)
|
|
123
|
+
private Long id;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 用户名
|
|
127
|
+
*/
|
|
128
|
+
private String username;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 邮箱
|
|
132
|
+
*/
|
|
133
|
+
private String email;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 状态:0-禁用,1-启用
|
|
137
|
+
*/
|
|
138
|
+
private Integer status;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 创建时间(自动填充)
|
|
142
|
+
*/
|
|
143
|
+
@TableField(fill = FieldFill.INSERT)
|
|
144
|
+
private LocalDateTime createTime;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 更新时间(自动填充)
|
|
148
|
+
*/
|
|
149
|
+
@TableField(fill = FieldFill.INSERT_UPDATE)
|
|
150
|
+
private LocalDateTime updateTime;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 逻辑删除标识
|
|
154
|
+
*/
|
|
155
|
+
@TableLogic
|
|
156
|
+
private Integer deleted;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Mapper 接口
|
|
161
|
+
|
|
162
|
+
```java
|
|
163
|
+
package com.example.mapper;
|
|
164
|
+
|
|
165
|
+
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
166
|
+
import com.example.entity.User;
|
|
167
|
+
import org.apache.ibatis.annotations.Mapper;
|
|
168
|
+
import org.apache.ibatis.annotations.Param;
|
|
169
|
+
import org.apache.ibatis.annotations.Select;
|
|
170
|
+
|
|
171
|
+
import java.util.List;
|
|
172
|
+
|
|
173
|
+
@Mapper
|
|
174
|
+
public interface UserMapper extends BaseMapper<User> {
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 自定义查询(注解方式)
|
|
178
|
+
*/
|
|
179
|
+
@Select("SELECT * FROM t_user WHERE status = #{status}")
|
|
180
|
+
List<User> selectByStatus(@Param("status") Integer status);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Service 接口与实现
|
|
185
|
+
|
|
186
|
+
```java
|
|
187
|
+
package com.example.service;
|
|
188
|
+
|
|
189
|
+
import com.baomidou.mybatisplus.extension.service.IService;
|
|
190
|
+
import com.example.entity.User;
|
|
191
|
+
|
|
192
|
+
public interface UserService extends IService<User> {
|
|
193
|
+
/**
|
|
194
|
+
* 根据用户名查询
|
|
195
|
+
*/
|
|
196
|
+
User getByUsername(String username);
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```java
|
|
201
|
+
package com.example.service.impl;
|
|
202
|
+
|
|
203
|
+
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
204
|
+
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
205
|
+
import com.example.entity.User;
|
|
206
|
+
import com.example.mapper.UserMapper;
|
|
207
|
+
import com.example.service.UserService;
|
|
208
|
+
import org.springframework.stereotype.Service;
|
|
209
|
+
|
|
210
|
+
@Service
|
|
211
|
+
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
|
|
212
|
+
|
|
213
|
+
@Override
|
|
214
|
+
public User getByUsername(String username) {
|
|
215
|
+
return this.getOne(new LambdaQueryWrapper<User>()
|
|
216
|
+
.eq(User::getUsername, username));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 分页查询
|
|
222
|
+
|
|
223
|
+
```java
|
|
224
|
+
package com.example.config;
|
|
225
|
+
|
|
226
|
+
import com.baomidou.mybatisplus.annotation.DbType;
|
|
227
|
+
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
|
228
|
+
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
|
229
|
+
import org.springframework.context.annotation.Bean;
|
|
230
|
+
import org.springframework.context.annotation.Configuration;
|
|
231
|
+
|
|
232
|
+
@Configuration
|
|
233
|
+
public class MybatisPlusConfig {
|
|
234
|
+
|
|
235
|
+
@Bean
|
|
236
|
+
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
|
237
|
+
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
|
238
|
+
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
|
239
|
+
return interceptor;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```java
|
|
245
|
+
@Service
|
|
246
|
+
@RequiredArgsConstructor
|
|
247
|
+
public class UserQueryService {
|
|
248
|
+
|
|
249
|
+
private final UserService userService;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* 分页查询用户
|
|
253
|
+
*/
|
|
254
|
+
public IPage<User> pageQuery(int current, int size, String username) {
|
|
255
|
+
Page<User> page = new Page<>(current, size);
|
|
256
|
+
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
|
|
257
|
+
.like(StringUtils.isNotBlank(username), User::getUsername, username)
|
|
258
|
+
.eq(User::getDeleted, 0)
|
|
259
|
+
.orderByDesc(User::getCreateTime);
|
|
260
|
+
return userService.page(page, wrapper);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Druid 监控面板
|
|
266
|
+
|
|
267
|
+
启动应用后,访问 `http://localhost:8080/druid/` 即可查看 Druid 监控面板。
|
|
268
|
+
|
|
269
|
+
### 监控内容
|
|
270
|
+
|
|
271
|
+
| 面板 | 功能 |
|
|
272
|
+
|------|------|
|
|
273
|
+
| 数据源 | 连接池状态、活跃连接数、等待线程数 |
|
|
274
|
+
| SQL 监控 | SQL 执行次数、耗时统计、慢 SQL 列表 |
|
|
275
|
+
| SQL 防火墙 | 拦截的危险 SQL 统计 |
|
|
276
|
+
| Web 应用 | 请求 URI 统计、关联 SQL |
|
|
277
|
+
| Spring 监控 | Spring Bean 方法调用统计 |
|
|
278
|
+
|
|
279
|
+
### 安全配置
|
|
280
|
+
|
|
281
|
+
生产环境建议限制访问:
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
spring:
|
|
285
|
+
datasource:
|
|
286
|
+
druid:
|
|
287
|
+
stat-view-servlet:
|
|
288
|
+
enabled: true
|
|
289
|
+
allow: 10.0.0.0/8 # 仅内网访问
|
|
290
|
+
deny: ""
|
|
291
|
+
login-username: ${DRUID_USERNAME}
|
|
292
|
+
login-password: ${DRUID_PASSWORD}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## 事务管理
|
|
296
|
+
|
|
297
|
+
### @Transactional 使用规范
|
|
298
|
+
|
|
299
|
+
```java
|
|
300
|
+
@Service
|
|
301
|
+
@RequiredArgsConstructor
|
|
302
|
+
public class OrderService {
|
|
303
|
+
|
|
304
|
+
private final OrderMapper orderMapper;
|
|
305
|
+
private final OrderItemMapper orderItemMapper;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* 创建订单(事务)
|
|
309
|
+
* 注意:@Transactional 默认只回滚 RuntimeException
|
|
310
|
+
*/
|
|
311
|
+
@Transactional(rollbackFor = Exception.class)
|
|
312
|
+
public Long createOrder(OrderDTO dto) {
|
|
313
|
+
// 1. 创建主订单
|
|
314
|
+
Order order = new Order();
|
|
315
|
+
order.setOrderNo(generateOrderNo());
|
|
316
|
+
order.setTotalAmount(dto.getTotalAmount());
|
|
317
|
+
orderMapper.insert(order);
|
|
318
|
+
|
|
319
|
+
// 2. 创建订单明细
|
|
320
|
+
for (OrderItemDTO item : dto.getItems()) {
|
|
321
|
+
OrderItem orderItem = new OrderItem();
|
|
322
|
+
orderItem.setOrderId(order.getId());
|
|
323
|
+
orderItem.setProductId(item.getProductId());
|
|
324
|
+
orderItem.setQuantity(item.getQuantity());
|
|
325
|
+
orderItemMapper.insert(orderItem);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return order.getId();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### 事务最佳实践
|
|
334
|
+
|
|
335
|
+
1. **必须指定 `rollbackFor = Exception.class`**:默认只回滚 RuntimeException
|
|
336
|
+
2. **避免在事务方法中调用同类方法**:Spring AOP 代理限制,同类内部调用不生效
|
|
337
|
+
3. **事务方法必须是 public**:非 public 方法事务不生效
|
|
338
|
+
4. **控制事务粒度**:不要在大方法上加 @Transactional,只在需要事务的方法上使用
|
|
339
|
+
5. **避免事务中做耗时操作**:如 HTTP 调用、文件上传,会长时间占用数据库连接
|
|
340
|
+
|
|
341
|
+
```java
|
|
342
|
+
// 错误示例:事务中调用远程服务
|
|
343
|
+
@Transactional(rollbackFor = Exception.class)
|
|
344
|
+
public void badExample() {
|
|
345
|
+
orderMapper.insert(order);
|
|
346
|
+
// 不要在事务中做远程调用!
|
|
347
|
+
httpClient.callRemoteService(); // 如果超时,数据库连接被长时间占用
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 正确示例:将远程调用移出事务
|
|
351
|
+
public void goodExample() {
|
|
352
|
+
createOrderInTransaction(order);
|
|
353
|
+
httpClient.callRemoteService(); // 事务外调用
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
@Transactional(rollbackFor = Exception.class)
|
|
357
|
+
public void createOrderInTransaction(Order order) {
|
|
358
|
+
orderMapper.insert(order);
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## 多数据源配置
|
|
363
|
+
|
|
364
|
+
### 配置文件
|
|
365
|
+
|
|
366
|
+
```yaml
|
|
367
|
+
spring:
|
|
368
|
+
datasource:
|
|
369
|
+
primary:
|
|
370
|
+
type: com.alibaba.druid.pool.DruidDataSource
|
|
371
|
+
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
372
|
+
url: jdbc:mysql://localhost:3306/primary_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
|
373
|
+
username: root
|
|
374
|
+
password: password1
|
|
375
|
+
druid:
|
|
376
|
+
initial-size: 5
|
|
377
|
+
max-active: 20
|
|
378
|
+
secondary:
|
|
379
|
+
type: com.alibaba.druid.pool.DruidDataSource
|
|
380
|
+
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
381
|
+
url: jdbc:mysql://localhost:3306/secondary_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
|
382
|
+
username: root
|
|
383
|
+
password: password2
|
|
384
|
+
druid:
|
|
385
|
+
initial-size: 5
|
|
386
|
+
max-active: 20
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 数据源配置类
|
|
390
|
+
|
|
391
|
+
```java
|
|
392
|
+
package com.example.config;
|
|
393
|
+
|
|
394
|
+
import com.alibaba.druid.pool.DruidDataSource;
|
|
395
|
+
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
|
396
|
+
import org.apache.ibatis.session.SqlSessionFactory;
|
|
397
|
+
import org.mybatis.spring.annotation.MapperScan;
|
|
398
|
+
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
399
|
+
import org.springframework.context.annotation.Bean;
|
|
400
|
+
import org.springframework.context.annotation.Configuration;
|
|
401
|
+
import org.springframework.context.annotation.Primary;
|
|
402
|
+
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
|
403
|
+
|
|
404
|
+
import javax.sql.DataSource;
|
|
405
|
+
|
|
406
|
+
@Configuration
|
|
407
|
+
@MapperScan(basePackages = "com.example.mapper.primary",
|
|
408
|
+
sqlSessionFactoryRef = "primarySqlSessionFactory")
|
|
409
|
+
public class PrimaryDataSourceConfig {
|
|
410
|
+
|
|
411
|
+
@Primary
|
|
412
|
+
@Bean("primaryDataSource")
|
|
413
|
+
@ConfigurationProperties(prefix = "spring.datasource.primary")
|
|
414
|
+
public DataSource primaryDataSource() {
|
|
415
|
+
return new DruidDataSource();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
@Primary
|
|
419
|
+
@Bean("primarySqlSessionFactory")
|
|
420
|
+
public SqlSessionFactory primarySqlSessionFactory(DataSource primaryDataSource) throws Exception {
|
|
421
|
+
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
|
|
422
|
+
factory.setDataSource(primaryDataSource);
|
|
423
|
+
factory.setMapperLocations(
|
|
424
|
+
new PathMatchingResourcePatternResolver()
|
|
425
|
+
.getResources("classpath:mapper/primary/**/*.xml"));
|
|
426
|
+
return factory.getObject();
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## 自动填充配置
|
|
432
|
+
|
|
433
|
+
```java
|
|
434
|
+
package com.example.config;
|
|
435
|
+
|
|
436
|
+
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
|
437
|
+
import lombok.extern.slf4j.Slf4j;
|
|
438
|
+
import org.apache.ibatis.reflection.MetaObject;
|
|
439
|
+
import org.springframework.stereotype.Component;
|
|
440
|
+
|
|
441
|
+
import java.time.LocalDateTime;
|
|
442
|
+
|
|
443
|
+
@Slf4j
|
|
444
|
+
@Component
|
|
445
|
+
public class MyMetaObjectHandler implements MetaObjectHandler {
|
|
446
|
+
|
|
447
|
+
@Override
|
|
448
|
+
public void insertFill(MetaObject metaObject) {
|
|
449
|
+
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
|
|
450
|
+
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
@Override
|
|
454
|
+
public void updateFill(MetaObject metaObject) {
|
|
455
|
+
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## 慢 SQL 排查
|
|
461
|
+
|
|
462
|
+
### 1. Druid 慢 SQL 监控
|
|
463
|
+
|
|
464
|
+
通过 Druid 监控面板查看:
|
|
465
|
+
- 访问 `/druid/sql.html` 查看所有 SQL 统计
|
|
466
|
+
- 按执行时间排序,找到慢 SQL
|
|
467
|
+
- 查看 SQL 执行计划和参数
|
|
468
|
+
|
|
469
|
+
### 2. MySQL EXPLAIN 分析
|
|
470
|
+
|
|
471
|
+
```sql
|
|
472
|
+
-- 分析查询执行计划
|
|
473
|
+
EXPLAIN SELECT * FROM t_user WHERE username = 'zhangsan';
|
|
474
|
+
|
|
475
|
+
-- 关注以下字段:
|
|
476
|
+
-- type: ALL(全表扫描,需优化)、index、range、ref、const
|
|
477
|
+
-- rows: 扫描行数
|
|
478
|
+
-- Extra: Using filesort(需优化)、Using temporary(需优化)
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### 3. 索引优化建议
|
|
482
|
+
|
|
483
|
+
- 为 WHERE 条件的字段添加索引
|
|
484
|
+
- 避免在索引列上使用函数或计算
|
|
485
|
+
- 联合索引遵循最左前缀原则
|
|
486
|
+
- 使用覆盖索引减少回表查询
|
|
487
|
+
|
|
488
|
+
## 最佳实践
|
|
489
|
+
|
|
490
|
+
### 1. 连接池配置
|
|
491
|
+
|
|
492
|
+
- **initial-size 和 min-idle 相同**:避免连接池频繁创建/销毁连接
|
|
493
|
+
- **max-active 合理设置**:建议 20-50,根据服务器配置和并发量调整
|
|
494
|
+
- **启用 test-while-idle**:定期检测空闲连接有效性
|
|
495
|
+
- **设置 max-wait**:避免无限等待连接
|
|
496
|
+
|
|
497
|
+
### 2. SQL 规范
|
|
498
|
+
|
|
499
|
+
- **禁止 SELECT ***:明确指定需要的列
|
|
500
|
+
- **分页查询必须加 LIMIT**:避免全表查询
|
|
501
|
+
- **批量操作使用 batch**:MyBatis Plus 的 `saveBatch()` 方法
|
|
502
|
+
- **大表查询走索引**:通过 EXPLAIN 验证
|
|
503
|
+
|
|
504
|
+
### 3. MyBatis Plus 使用
|
|
505
|
+
|
|
506
|
+
- **优先使用 LambdaQueryWrapper**:类型安全,重构友好
|
|
507
|
+
- **合理使用逻辑删除**:高频查询表注意索引包含 deleted 字段
|
|
508
|
+
- **Entity 实现 Serializable**:支持缓存序列化
|
|
509
|
+
- **使用自动填充**:createTime、updateTime 统一处理
|
|
510
|
+
|
|
511
|
+
## 常见问题
|
|
512
|
+
|
|
513
|
+
### 1. 连接池耗尽(Pool exhausted)
|
|
514
|
+
|
|
515
|
+
**原因**:并发量过大或连接泄漏
|
|
516
|
+
|
|
517
|
+
**解决**:
|
|
518
|
+
- 增大 `max-active` 配置
|
|
519
|
+
- 检查是否有未关闭的连接(使用 try-with-resources)
|
|
520
|
+
- 启用 Druid 的 `removeAbandoned` 配置回收泄漏连接
|
|
521
|
+
- 检查事务方法中是否有耗时操作占用连接
|
|
522
|
+
|
|
523
|
+
### 2. 连接超时(Connection timeout)
|
|
524
|
+
|
|
525
|
+
**原因**:MySQL 服务端关闭了空闲连接
|
|
526
|
+
|
|
527
|
+
**解决**:
|
|
528
|
+
- 启用 `test-while-idle: true` 检测空闲连接
|
|
529
|
+
- 设置 `time-between-eviction-runs-millis` 小于 MySQL 的 `wait_timeout`
|
|
530
|
+
- 检查 MySQL `max_connections` 配置
|
|
531
|
+
|
|
532
|
+
### 3. 慢查询导致性能问题
|
|
533
|
+
|
|
534
|
+
**原因**:缺少索引或 SQL 不合理
|
|
535
|
+
|
|
536
|
+
**解决**:
|
|
537
|
+
- 通过 Druid 监控面板定位慢 SQL
|
|
538
|
+
- 使用 EXPLAIN 分析执行计划
|
|
539
|
+
- 添加合适的索引
|
|
540
|
+
- 优化 SQL 写法,避免全表扫描
|
|
541
|
+
|
|
542
|
+
### 4. MyBatis Plus 逻辑删除不生效
|
|
543
|
+
|
|
544
|
+
**原因**:配置缺失或字段注解错误
|
|
545
|
+
|
|
546
|
+
**解决**:
|
|
547
|
+
- 检查 `global-config.db-config.logic-delete-field` 配置
|
|
548
|
+
- 确保实体类字段添加 `@TableLogic` 注解
|
|
549
|
+
- 自定义 SQL 不会自动添加逻辑删除条件,需手动处理
|
|
550
|
+
|
|
551
|
+
### 5. 批量插入性能差
|
|
552
|
+
|
|
553
|
+
**原因**:逐条插入产生大量 SQL
|
|
554
|
+
|
|
555
|
+
**解决**:
|
|
556
|
+
- 使用 `saveBatch()` 方法批量插入
|
|
557
|
+
- JDBC URL 添加 `rewriteBatchedStatements=true` 参数
|
|
558
|
+
- 控制每批次大小(推荐 500-1000 条)
|
|
559
|
+
|
|
560
|
+
## 依赖版本
|
|
561
|
+
|
|
562
|
+
- Druid: 1.2.24
|
|
563
|
+
- MyBatis Plus: 3.5.15(mybatis-plus-spring-boot3-starter)
|
|
564
|
+
- MySQL Connector/J: 由 Spring Boot 3.5.3 BOM 管理
|
|
565
|
+
- Spring Boot: 3.5.3
|
|
566
|
+
- Java: 21
|
|
567
|
+
|
|
568
|
+
## 参考文档
|
|
569
|
+
|
|
570
|
+
- [MyBatis Plus 官方文档](https://baomidou.com/pages/24112f/)
|
|
571
|
+
- [Druid 官方文档](https://github.com/alibaba/druid/wiki)
|
|
572
|
+
- [Spring Boot 数据访问](https://docs.spring.io/spring-boot/reference/data/sql.html)
|