Skip to content

Commit 231a770

Browse files
authored
🐛 fix(codegen): 补齐自增主键模板上下文标记 (#172)
* 🐛 fix(codegen): 补齐自增主键模板上下文标记 * 🐛 fix(generator): 修正遗留实体上下文自增标志 (#174) * 🐛 fix(generator): 修正遗留实体上下文自增标志 * 🐛 fix(generator): 保留 Java 属性名内部大小写 (#176) * 🐛 fix(generator): 保留 Java 属性名内部大小写 * 🐛 fix(template): 仅为自增主键启用 generated keys (#178) * 🐛 fix(template): 仅为自增主键启用 generated keys * 🐛 fix(codegen): 统一主键列表与列上下文 (#180) * 🐛 fix(codegen): 统一主键列表与列上下文 * 🐛 fix(mcp): 统一描述与代码生成类型映射 (#182) * 🐛 fix(mcp): 统一描述与代码生成类型映射 * 🐛 fix(codegen): 统一旧入口方言表名发现 (#184) * 🐛 fix(codegen): 统一旧入口方言表名发现 * ♻️ refactor(codegen): 复用统一表名元数据合同 (#186) * ♻️ refactor(codegen): 复用统一表名元数据合同 * 🐛 fix(codegen): 补齐批量分析表名上下文 (#188) * 🐛 fix(codegen): 补齐批量分析表名上下文 * 🐛 fix(database): 持久化 SQLite execute_query 写入 (#190) * 🐛 fix(database): 持久化 SQLite execute_query 写入 * 🐛 fix(governance): 对齐工具注册表数据库能力 (#192) * 🐛 fix(governance): 对齐工具注册表数据库能力 * 🔒 security(mcp-apps): 收紧代码 diff 路径边界 (#194) * 🔒 security(mcp-apps): 收紧代码 diff 路径边界 * 🐛 fix(codegen): 对齐生成文件写入统计与结果状态 (#196) * 🐛 fix(codegen): 对齐生成文件写入统计与结果状态 * ✨ feat(codegen): 统一 DTO 与 VO 生成选项合同 (#198) * ✨ feat(codegen): 统一 DTO 与 VO 生成选项合同 * 📚 docs(governance): 固化 GitHub 元数据正文门禁 (#200) * 📚 docs(governance): 固化 GitHub 元数据正文门禁 * 🐛 fix(database): 回滚失败 SQLite 查询事务 (#202) * 🐛 fix(database): 回滚失败 SQLite 查询事务 * 🐛 fix(schema): 规范图算法重复表输入 (#204) * 🐛 fix(schema): 规范图算法重复表输入 * 🐛 fix(dependencies): 隔离版本分析状态 (#206) * 🐛 fix(dependencies): 隔离版本分析状态 * 🐛 fix(cli): 统一解析 MCP 响应 (#208) * 🐛 fix(cli): 统一解析 MCP 响应 * ✨ feat(connection): 增加 MCP 连接释放工具 (#210) * ✨ feat(connection): 增加 MCP 连接释放工具 * ⚡ perf(mcp): 隔离同步数据库调用与事件循环 (#212)
1 parent 369cf94 commit 231a770

60 files changed

Lines changed: 2655 additions & 550 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/skills/java-codegen-from-db/SKILL.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ LLM **必须**把 schema 概述、命名推断和模板推荐呈现给用户,
216216

217217
如果目标文件已存在,生成 `*.codegen.bak` 备份,告诉用户怎么回滚(`mv x.bak x`)。
218218

219+
### 5.4 释放连接
220+
221+
工作流结束或用户决定中止时,调用 **`db_disconnect`** with
222+
`{"connection_id": "<connection_id>"}`。释放后的 ID 不应继续用于查询或生成。
223+
219224
---
220225

221226
## 错误处理与重试规则
@@ -252,6 +257,7 @@ LLM **必须**把 schema 概述、命名推断和模板推荐呈现给用户,
252257
codegen_render_dto (×1 per 表, 仅 sb35-java21)
253258
254259
[写盘] (内置于上述工具,Phase 3 后拆出 file_write_to_project)
260+
[收尾] db_disconnect (会话结束时释放连接)
255261
```
256262

257263
## 设计原则

‎.github/workflows/metadata.yml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Repository metadata
2+
3+
on:
4+
issues:
5+
types: [opened, edited, reopened]
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
issue-policy:
12+
name: Issue title and body policy
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout validation script
16+
uses: actions/checkout@v4
17+
18+
- name: Validate Issue metadata
19+
env:
20+
ISSUE_EVENT: ${{ github.event_path }}
21+
run: python scripts/validate_commit_title.py --issue-event "$ISSUE_EVENT"

‎CONTRIBUTING.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,8 @@ PYTHONPATH=src uv run python scripts/verify_java_compile.py
3636
:test_tube: test(generator): 覆盖可空枚举字段
3737
```
3838

39-
正文依次写 `背景`、`变更`、`验证`、`实验`、`风险`。PR 标题和首个提交标题必须相同风格;PR 额外关联 Issue 并填写审查清单。
39+
Bug Issue 依次写 `版本与环境`、`问题与预期行为`、`最小复现`、`验收标准`、`非目标、风险与安全`;
40+
功能或架构 Issue 使用 `问题与用户价值`、`建议方案与替代方案`、`验收标准`、`架构、兼容性与测试计划`、
41+
`非目标与风险`。PR 必须按模板中的七个固定章节填写,且每节有实际内容;实现完成和验证完成各发一条
42+
包含结论、取舍、命令/结果、风险和下一步的回帖。Issue 编辑会触发轻量元数据检查,不能使用空章节、
43+
连续 `??`、替代字符或字面量 `\\r\\n` 代替 Markdown 换行。

‎README.es-ES.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ En el cliente LLM, di "**Genera código Spring Boot a partir de las tres tablas
8686
5. Llamará a `ai_recommend_template` para recomendar → detectará RBAC y recomendará `MybatisPlus-Mixed`
8787
6. Usará `codegen_build_context` + 5 `codegen_render_*` para generar por capas, devolviendo code-diff en cada capa
8888
7. Tras la confirmación del usuario, escribirá en disco
89+
8. Al finalizar la sesión, llamará a `db_disconnect(connection_id)` para liberar la conexión
8990

9091
## Capacidades principales (Fase 1 → 5)
9192

@@ -126,11 +127,11 @@ En el cliente LLM, di "**Genera código Spring Boot a partir de las tres tablas
126127
- Logs estructurados: `DBJAVAGENIX_LOG_FORMAT=json` permite salida de JSON en una sola línea, ideal para Loki/ELK
127128
- [Manual de despliegue](docs/deployment.md): 3 modos de despliegue + 6 escenarios de troubleshooting
128129

129-
## Resumen de herramientas (33 en total)
130+
## Resumen de herramientas (34 en total)
130131

131132
| Categoría | Herramienta |
132133
|------|------|
133-
| Conexión / Consulta | db_connect_test / db_query_databases / db_query_tables / db_query_table_exists / db_query_execute |
134+
| Conexión / Consulta | db_connect_test / db_disconnect / db_query_databases / db_query_tables / db_query_table_exists / db_query_execute |
134135
| Estructura de tabla | db_table_describe / db_table_columns / db_table_primary_keys / db_table_foreign_keys / db_table_indexes |
135136
| Algoritmos de grafo de schema | schema_topo_order / schema_cluster_tables / schema_check_cycles |
136137
| Generación de código (atómica) | codegen_build_context / codegen_render_entity / codegen_render_dao / codegen_render_service / codegen_render_controller / codegen_render_dto / codegen_render_mapper |
@@ -162,7 +163,7 @@ Consulta [`iteration-plan/01-target-architecture.md`](iteration-plan/01-target-a
162163
```
163164
[ Capa Skills ] Define "cómo hacerlo" — .claude/skills/*.md Flujo de 5 fases explícito
164165
↓
165-
[ Capa MCP ] Proporciona "qué se puede hacer" — 33 herramientas Contexto transferido explícitamente
166+
[ Capa MCP ] Proporciona "qué se puede hacer" — 34 herramientas Contexto transferido explícitamente
166167
↓
167168
[ Capa Apps ] Hace los resultados "visibles" — 4 componentes de UI (mermaid/dashboard/code-diff/tree)
168169
```

‎README.ja-JP.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ LLM クライアントで、例えば **「myapp データベースの sys_user
8585
5. `ai_recommend_template` でテンプレートを推薦し、RBAC を検出して `MybatisPlus-Mixed` を提案する
8686
6. `codegen_build_context` + 5 つの `codegen_render_*` でレイヤーごとに生成し、各レイヤーの code-diff を返す
8787
7. ユーザーの確認後にファイルへ書き込む
88+
8. セッション終了時に `db_disconnect(connection_id)` を呼び出して接続を解放する
8889

8990
## 主な機能(Phase 1 → 5)
9091

@@ -130,11 +131,11 @@ LLM クライアントで、例えば **「myapp データベースの sys_user
130131
- 構造化ログ: `DBJAVAGENIX_LOG_FORMAT=json` で Loki / ELK に適した 1 行 JSON を出力
131132
- [デプロイガイド](docs/deployment.md): 3 つのデプロイ方式 + 6 つのトラブルシューティング事例
132133

133-
## ツール一覧(33 個)
134+
## ツール一覧(34 個)
134135

135136
| カテゴリ | ツール |
136137
|------|------|
137-
| 接続 / クエリ | db_connect_test / db_query_databases / db_query_tables / db_query_table_exists / db_query_execute |
138+
| 接続 / クエリ | db_connect_test / db_disconnect / db_query_databases / db_query_tables / db_query_table_exists / db_query_execute |
138139
| テーブル構造 | db_table_describe / db_table_columns / db_table_primary_keys / db_table_foreign_keys / db_table_indexes |
139140
| スキーマグラフアルゴリズム | schema_topo_order / schema_cluster_tables / schema_check_cycles |
140141
| コード生成(アトミック) | codegen_build_context / codegen_render_entity / codegen_render_dao / codegen_render_service / codegen_render_controller / codegen_render_dto / codegen_render_mapper |
@@ -166,7 +167,7 @@ LLM クライアントで、例えば **「myapp データベースの sys_user
166167
```
167168
[ Skills 層 ] 「方法」を定義 — .claude/skills/*.md 明示的な 5 段階ワークフロー
168169
↓
169-
[ MCP 層 ] 「できること」を提供 — 33 ツール context を明示的に受け渡し
170+
[ MCP 層 ] 「できること」を提供 — 34 ツール context を明示的に受け渡し
170171
↓
171172
[ Apps 層 ] 結果を「見える化」 — 4 つの UI コンポーネント(mermaid/dashboard/code-diff/tree)
172173
```

‎README.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ graph LR
1313
Skills[".claude/skills/<br/>java-codegen-from-db<br/>springboot-migration"]
1414
Skills -->|按需调用| MCP
1515
16-
subgraph MCP[MCP Server 33 工具]
16+
subgraph MCP[MCP Server 34 工具]
1717
direction TB
1818
DB[db_* 连接 / 查询 / 描述]
1919
Atom[codegen_build_context<br/>codegen_render_entity/dao/service/<br/>controller/dto/mapper]
@@ -90,6 +90,7 @@ SQL Server 的类型映射保留为后续扩展准备,但尚未实现运行时
9090
5. 调用 `ai_recommend_template` 推荐 → 检测到 RBAC,推 `MybatisPlus-Mixed`
9191
6. 用 `codegen_build_context` + 6 个 `codegen_render_*` 分层生成,每层返回 code-diff
9292
7. 用户确认后写盘
93+
8. 会话结束时调用 `db_disconnect(connection_id)` 释放连接
9394

9495
## 核心能力 (Phase 1 → 5)
9596

@@ -130,11 +131,11 @@ SQL Server 的类型映射保留为后续扩展准备,但尚未实现运行时
130131
- 结构化日志: `DBJAVAGENIX_LOG_FORMAT=json` 可输出单行 JSON,适合 Loki/ELK
131132
- [部署手册](docs/deployment.md): 3 种部署模式 + 6 个排障场景
132133

133-
## 工具总览 (33 个)
134+
## 工具总览 (34 个)
134135

135136
| 类别 | 工具 |
136137
|------|------|
137-
| 连接 / 查询 | db_connect_test / db_query_databases / db_query_tables / db_query_table_exists / db_query_execute |
138+
| 连接 / 查询 | db_connect_test / db_disconnect / db_query_databases / db_query_tables / db_query_table_exists / db_query_execute |
138139
| 表结构 | db_table_describe / db_table_columns / db_table_primary_keys / db_table_foreign_keys / db_table_indexes |
139140
| Schema 图算法 | schema_topo_order / schema_cluster_tables / schema_check_cycles |
140141
| 代码生成 (atomic) | codegen_build_context / codegen_render_entity / codegen_render_dao / codegen_render_service / codegen_render_controller / codegen_render_dto / codegen_render_mapper |
@@ -166,7 +167,7 @@ SQL Server 的类型映射保留为后续扩展准备,但尚未实现运行时
166167
```
167168
[ Skills 层 ] 定义"怎么做" — .claude/skills/*.md 显式 5 阶段工作流
168169
↓
169-
[ MCP 层 ] 提供"能做什么" — 33 个原子工具 context 显式传递
170+
[ MCP 层 ] 提供"能做什么" — 34 个原子工具 context 显式传递
170171
↓
171172
[ Apps 层 ] 让结果"看得见" — 4 个 UI 组件 (mermaid/dashboard/code-diff/tree)
172173
```
@@ -187,7 +188,7 @@ SQL Server 的类型映射保留为后续扩展准备,但尚未实现运行时
187188
| [docs/screenshots/README.md](docs/screenshots/README.md) | MCP Apps 4 组件客户端兼容性 |
188189
| [docs/algorithms-overview.md](docs/algorithms-overview.md) | v0.2.1 schema 图算法 (topo / cluster / cycle) |
189190
| [docs/design-patterns-catalog.md](docs/design-patterns-catalog.md) | 生成器与生成代码中的设计模式 |
190-
| [docs/adr/](docs/adr/) | 14 个 ADR (架构 / 原子 / 渐进 / 规则 / 不引依赖 / schema 算法 / 规范配置 / MCP v3 / 1h 缓存 / agentic / 多方言 / SDK 契约 / 工具契约 / 元数据契约) |
191+
| [docs/adr/](docs/adr/) | 15 个 ADR (架构 / 原子 / 渐进 / 规则 / 不引依赖 / schema 算法 / 规范配置 / MCP v3 / 1h 缓存 / agentic / 多方言 / SDK 契约 / 工具契约 / 元数据契约 / 连接生命周期) |
191192
| [.claude/skills/java-codegen-from-db/SKILL.md](.claude/skills/java-codegen-from-db/SKILL.md) | 主 Skill: 代码生成 5 阶段工作流 |
192193
| [.claude/skills/springboot-migration/SKILL.md](.claude/skills/springboot-migration/SKILL.md) | 第二 Skill: Spring Boot 2.7→3.x 迁移 |
193194

‎docs/adr/011-multi-dialect-strategy.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,14 @@ postgresql:
137137
- 36 个 unit test 覆盖 mysql/postgres 两套映射,跨方言隔离测试防串
138138
- D3 用真实 PG container 验证 information_schema 上报的字符串确实命中
139139
我们的 key (这是配置驱动方案做不到的)
140-
- `template_context.py` 之后会渐进迁移到调用 `get_dialect(...).java_type_for()`,
141-
本 ADR 不强制一次性切换
140+
- `template_context.py`、MCP `db_table_describe` 已统一调用
141+
`get_dialect(...).java_type_for()`;未注册运行时方言仍保留旧 YAML 回退,避免把
142+
SQL Server/Oracle 的配置映射误报为连接能力
142143

143144
**坏**:
144145
- `dialect.py` 文件略大 (~300 行,主要是两张映射表),但都是数据
145-
- `template_context.py` 现在有重复的 MySQL 映射,**v0.3.1 计划重构** — 不在这个
146-
ADR 范围内,先把 PG 跑起来,old code 保留向后兼容
146+
- MCP 描述工具对已注册方言不再维护独立的 Java 类型表;Java 类型对应 imports
147+
也由 `DialectAdapter.java_imports_for()` 统一提供
147148

148149
**实测**:
149150
- D3 PG 16 实测 23 个 PG 类型全部命中预期 Java 类型
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# ADR-015: MCP 连接生命周期与显式释放
2+
3+
- **状态**: Accepted
4+
- **日期**: 2026-09-09
5+
- **关联 Issue**: #209
6+
- **关联实现**: `src/dbjavagenix/database/mcp_tools.py`
7+
8+
## 背景
9+
10+
`ConnectionManager` 已经能够按 connection ID 关闭连接,但公共 MCP 合同只
11+
暴露 `db_connect_test`。长生命周期客户端无法在探索完成、重试失败或切换数据库
12+
时主动释放连接,连接对象和脱敏配置会一直保留到 server 进程退出。
13+
14+
## 决定
15+
16+
新增 `db_disconnect(connection_id)` 作为显式连接生命周期工具:
17+
18+
1. handler 只负责校验参数、调用 `ConnectionManager.close_connection()` 和序列化
19+
响应,不复制连接状态或驱动逻辑。
20+
2. 成功关闭返回 `success=true`;未知、空值和重复关闭统一返回
21+
`success=false`、`error=connection_not_found`。
22+
3. 关闭异常返回 `disconnect_failed`,异常文本经过现有凭据脱敏边界后再返回。
23+
4. 工具加入 canonical MCP 列表和 progressive discovery 元数据;默认 progressive
24+
可见集合不变,客户端可通过 `search_tools("disconnect")` 发现它。
25+
5. 客户端在会话结束时调用该工具;不引入连接池、TTL、后台回收或自动关闭策略。
26+
27+
## 备选方案
28+
29+
- 仅依赖进程退出时的 `__del__`:无法覆盖长会话中的提前释放和异常重试,已拒绝。
30+
- 增加后台 TTL:可能误杀仍在使用的连接,并引入不可预测的时序,已拒绝。
31+
- 在 handler 内直接操作驱动连接:会重复生命周期逻辑,绕过 `ConnectionManager`,已拒绝。
32+
33+
## 影响
34+
35+
- 连接建立和查询 API 保持兼容,旧客户端无需立即改造。
36+
- 释放后的 connection ID 不可继续使用;后续查询收到既有的连接不存在错误。
37+
- 显式释放降低长生命周期会话的资源占用,但不承诺性能收益或自动回收。
38+
39+
## 验证
40+
41+
- 单元测试覆盖工具 schema、成功关闭后的连接与配置清理、未知/空值/重复 ID、
42+
异常脱敏以及 canonical server dispatch。
43+
- 使用 SQLite in-memory manager 和 monkeypatch 验证,不伪造真实 MySQL/PostgreSQL
44+
连接释放结果。
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# ADR-016: 将同步数据库调用移出 MCP 事件循环
2+
3+
- **状态**: Accepted
4+
- **日期**: 2026-09-09
5+
- **关联 Issue**: #211
6+
- **关联实现**: `src/dbjavagenix/database/connection_manager.py`
7+
8+
## 背景
9+
10+
MCP handler 使用 `async def`,但数据库驱动和元数据 introspector 仍是同步实现。
11+
网络等待或大型 SQLite 查询会占住事件循环,使同一会话的健康检查、取消和其他连接请求
12+
延迟。SQLite 默认还会拒绝跨线程使用连接;多个 worker 直接共享一个连接也可能交叉使用
13+
cursor。
14+
15+
## 决定
16+
17+
1. 在 MCP 数据库边界统一使用 `asyncio.to_thread`;保留同步 `ConnectionManager` API,
18+
供 CLI、测试和外部调用方继续使用。
19+
2. `ConnectionManager` 为每个 connection ID 建立可重入锁,锁覆盖健康探测、cursor
20+
生命周期、SQL 执行和关闭;不同连接不共享这把锁。
21+
3. SQLite 连接启用 `check_same_thread=False`,但只有在上述连接锁保护下交给 worker。
22+
4. 异步 analyzer 通过 worker 中的短生命周期 event loop 执行,输入输出合同保持不变。
23+
5. 取消异步 handler 不强杀底层驱动调用;worker 返回后由 context manager 关闭 cursor,
24+
连接仍可被显式 `db_disconnect` 释放。
25+
26+
## 备选方案
27+
28+
- 替换原生异步 MySQL/PostgreSQL 驱动:迁移成本高且会改变方言、事务和依赖边界,暂不采用。
29+
- 只在 handler 外包线程、不加连接锁:会留下 SQLite thread-affinity 和 cursor 交叉风险,
30+
不采用。
31+
- 为每个请求建立新连接:增加连接开销并破坏现有 connection ID 生命周期,不采用。
32+
33+
## 影响
34+
35+
- 同步公共 API、SQL、事务提交/回滚、权限和 MCP 文本/Raw Response 保持不变。
36+
- 同一连接的数据库阶段串行,不同连接可以并行等待;不宣称生产吞吐提升。
37+
- worker 调度带来少量开销;驱动额外的线程绑定限制需要在真实 MySQL/PostgreSQL 环境
38+
单独验证。
39+
40+
## 验证
41+
42+
- 受控 60ms 阻塞 driver + heartbeat 证明事件循环仍获调度。
43+
- fake cursor 实验覆盖同连接最大并发为 1、跨连接同时进入和 close 等待 SQL body 完成。
44+
- SQLite in-memory worker 查询和现有 SQLite MCP/codegen 合同继续通过;真实网络数据库
45+
线程模型不在本地实验范围内。

‎docs/adr/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@
2525

2626
### v0.3 (数据库元数据契约)
2727
- [ADR-014: 统一数据库元数据描述契约](014-metadata-introspection-contract.md)
28+
- [ADR-015: MCP 连接生命周期与显式释放](015-connection-lifecycle.md)
29+
- [ADR-016: 将同步数据库调用移出 MCP 事件循环](016-async-database-boundary.md)

0 commit comments

Comments
 (0)