Chat2DB Java 服务端对象转换契约Converter 集中式映射规范与源码落地实践【免费下载链接】Chat2DBChat2DB is a free, cross-platform, local-first database client and SQL workspace for developers, DBAs, analysts, and data teams. Connect to 40 databases, manage data, edit and run SQL, and use your own AI model to generate, explain, and optimize queries. Available on desktop, web, Docker, and CLI, with MCP support.项目地址: https://gitcode.com/GitHub_Trending/ch/Chat2DB导读本文以 Chat2DB 仓库中 spec/code/server/java-object-converter-contracts.md 为骨架系统讲解服务端对象转换Object Conversion必须集中到 Converter 的工程规范包括转换的定义与边界、各模块 Converter 归属表、禁止的临时转换写法、纯映射Pure-Mapping规则、调用边界、允许的例外场景以及评审清单。读者学完本文能够理解 Chat2DB 多模块web / domain-core / storage / spi / plugins / tools之间为何必须通过converter包完成对象映射并能结合仓库真实代码如DbWebConverter、StorageConverter、CommandConverter快速落地这一规范。1. 目的为什么对象转换必须集中化Chat2DB 服务端是一个典型的分层多模块工程chat2db-community-webHTTP 层、chat2db-community-domain-core领域层、chat2db-community-storage存储层、chat2db-community-spi插件 SPI、chat2db-community-plugins/*数据库插件与chat2db-community-tools工具层。对象在层与层之间传递时字段名、类型、结构往往不一致例如 HTTP DTO 的user字段到领域请求里叫username、type到dbType见 DataSourceConverter 中的Mapping。契约第一条强制约束Controllers、Services、Adapters、Facades、存储实现、插件实现一律不得自行定义对象映射方法。理由有三避免字段映射散落各处——若每个类都写一段new Xxx() setter同一个字段的映射逻辑会重复出现在几十个文件里改动一个字段名要全局搜索修改。防止边界模型泄漏boundary-model leakage——服务层直接操作 Web DTO、存储层直接组装领域模型会让模块边界形同虚设。保证转换行为一致——集中到 Converter 后null 处理、字段名转换、类型转换只实现一次行为可预测、可测试。需要说明的是该契约仅约束Chat2DB 项目对象之间的结构化转换JDK、Spring、JDBC 驱动、ANTLR、JSON 库等第三方类型的使用不在其管辖范围内。2. 对象转换的定义什么算、什么不算契约给出明确判定标准只要一个项目对象被转换为另一个项目对象就属于对象转换必须使用 Converter。典型必须走 Converter 的场景Web 请求/响应 DTO 与领域请求/响应之间的双向转换领域请求/响应与领域模型之间的转换领域模型与存储模型、实体或 record 之间的转换Gateway、CLI、MCP、本地存储或插件结果对象与内部模型的转换List、分页、树结构中元素级别的转换同一语义对象跨层传输时字段名/类型不同、或需要轻度规整light normalization的转换。不属于强制 Converter 场景无需硬套 Converter值解析如toString()、枚举的fromCode/fromValue/fromName字符串、路径、SQL 片段、字节数组、原始 JDBC 值的格式化把异常适配为 HTTP 错误结果通用 JDK 集合工具方法如toMap、toList、toSet测试夹具、Mock 与断言对象构造。契约还特别提示即使方法名不含convert只要一个 helper 读取一个项目对象并返回另一个项目对象就属于对象转换不能靠命名规避规范。仓库中CommandConverter就是典型——它实现IDbSqlCommandService.toSqlExecuteRequest(...)方法名不带 convert但内部正是通过param2model完成DbDlExecuteRequest→SqlExecuteRequest的映射见 CommandConverter.java。3. Converter 归属每个转换都有唯一的家契约用一张归属表明确了“哪类转换放在哪个模块的 converter 包”这是整个规范最核心的可执行部分完整继承如下转换类型Converter 位置说明HTTP DTO ↔ 领域契约对象chat2db-community-web的converter包由 Controller、Web Facade、Adapter 调用领域契约对象 ↔ 领域模型chat2db-community-domain-core的converter包由 Service 实现调用领域模型 ↔ 持久化对象chat2db-community-storage的converter包由存储实现调用插件内部模型对应chat2db-community-plugins/*模块内的converter包仅插件内部使用不暴露给 web / domain-core共享 SPI 结果转换chat2db-community-spi中的converter包或显式 Converter 类型只能依赖 SPI、domain-api、tools 允许的模型工具层配置对象chat2db-community-tools中的显式*Converter只处理工具自有或第三方配置对象新增业务对象转换时统一放入converter包并命名为XxxConverter既有的不在converter包中的*Converter类型也必须遵守纯映射规则。仓库中每个模块都有对应的落地实例web 层DbWebConverter 位于web/api/converter/db包声明了request2param、dto2response、tableDto2response等几十个抽象映射方法使用 MapStructMapper(componentModel spring)在编译期生成实现。domain-core 层DataSourceConverter、CommandConverter 等位于domain/core/converter包。storage 层StorageConverter 位于storage/converter包负责DataSource↔WorkspaceDataSource、DataSourceNamespace↔WorkspaceDataSourceNamespace、DbTablePinRequest→PinTable等映射。spi 层DocumentConverter 位于spi/converter包负责把 MongoDB Document / Map 递归转换为LinkedHashMap含Binary、Blob、byte[]的特化处理。插件层MysqlRoutineConverter、RedisKeyConverter 等位于各插件模块内部。tools 层ConsoleObjectConverter、NetworkProxySettingsConverter 等显式*Converter类型。4. 禁止的临时转换Ad Hoc Conversion契约列出了七类绝对禁止在非 Converter 类中出现的写法任何一条命中即视为违反规范禁止自定义映射方法名非 Converter 类不得添加名为toXxx、fromXxx、convertXxx或xxx2yyy的对象映射方法。禁止手写 setter 拷贝不得通过new Xxx()后连续调用 setter 的方式从源对象拷贝字段。禁止用 Builder 组装不得通过 Builder 从另一个项目对象组装目标项目对象。禁止反射拷贝不得使用BeanUtils.copyProperties、BeanUtil.copyProperties、PropertyUtils.copyProperties做项目对象转换。禁止ObjectMapper.convertValue做项目对象转换。禁止 JSON 往返不得写JSON.parseObject(JSON.toJSONString(source), Target.class)这类序列化-反序列化来回。禁止直接使用 MapStruct非 Converter 类不得声明Mapper类型也不得直接调用Mappers.getMapper。契约给出的对照示例// 禁止在 Controller 里写私有转换方法。 private ModelConfigSaveRequest toModelConfigParam(WebModelConfigSaveRequest request) { ModelConfigSaveRequest param new ModelConfigSaveRequest(); param.setName(request.getName()); return param; } // 允许Controller 委托 Converter 完成转换。 ModelConfigSaveRequest param modelConfigWebConverter.request2param(request);结合仓库观察Mapper注解只出现在各模块converter包下的类中web 层如 ChatConverter、DataSourceWebConverter、OperationLogConverter 等domain-core 层如 SqlCompletionConverter这一分布本身正是规范第 4、5 节的可验证证据。5. 纯映射规则Converter 只做结构映射契约对 Converter 内部行为有严格约束保证其“纯粹性”只允许字段映射、集合元素映射、null 处理、字段名转换、轻量类型转换。可以使用MapStruct 的Mapping、Mappings、MappingTarget以及少量 default 方法。可以包含转换所需的轻度规整如枚举名转换、trim、展示脱敏、既定的加密/解密边界。禁止注入或调用服务、存储组件、Mapper、Repository、客户端或ApplicationContext。禁止执行权限校验、状态流转、对象存在性检查、跨模块业务编排。禁止吞异常返回默认对象转换失败必须抛显式异常。公共方法命名必须标明源和目标例如request2param、model2response、storage2model、toResponse。关键判定如果转换需要查库、远程调用或运行时上下文它就不是纯对象转换。此时应由业务 Service 先拿到完整的源对象再交给 Converter 做纯结构映射。仓库中的方法命名完全符合该约定DbWebConverter中的request2param(...)、tableDto2response(...)、schemaDto2response(...)、databaseDto2response(...)见 DbWebConverter.javaStorageConverter中的dataSource2workspace/workspace2dataSource见 StorageConverter.java。同时契约提示迁移期桥接模式Converter 为实现某个转换接口而临时实现时该接口只能作为桥不得借接口调用业务能力新代码不得扩大这种模式。仓库中的 CommandConverter 实现了IDbSqlCommandService接口但toSqlExecuteRequest只是转发到param2model正是这种受限桥接的典型示例。6. 调用边界谁可以调谁契约按层规定了调用边界防止绕过 Converter 组装他层内部模型Controller只负责 HTTP 绑定、校验触发与响应包装不得手拼领域请求。Web Adapter负责协议适配与调用编排不得手拼 Gateway、领域或 Web 模型转换。Service 实现负责业务编排与规则评估不得手拼响应、模型或实体转换。存储实现负责持久化调用不得手拼领域模型与存储对象的转换。插件实现负责提供插件能力插件模型转换必须放插件本地 Converter。跨模块调用方只能依赖目标层的公共契约对象与本层所属的 Converter不得绕过 Converter 组装另一层的内部模型。这一点与仓库模块边界设计见 spec/code/server/java-module-boundaries.md相辅相成Converter 的converter包位置决定了它只能访问所在层允许依赖的模型类型。例如 SPI 层的转换DocumentConverter不依赖任何 web / domain-core 的实现类插件层的 MysqlRoutineConverter 只做插件内部模型转换、不外泄。7. 允许的例外不需 Converter 但必须保持窄范围以下场景不需要 Converter但必须保持范围狭窄一旦开始从一个项目对象向另一个项目对象拷贝字段就必须迁入 Converter静态工厂对象上的静态工厂通过自身不变量约束创建不拷贝他层字段。枚举/值对象解析器返回自身类型如fromCode、fromValue。SQL/JDBC 原始值处理器返回String、byte[]、原始值或裸驱动对象例如 SPI 中 ResultSetConverter 这类面向 JDBC 原始值的工具。异常转换器把异常适配为 HTTP 错误结果。测试代码构造测试数据。启动装配/配置代码创建 Bean 或配置属性对象不映射业务对象。8. 评审清单如何检查对象转换是否合规契约最后给出了可落地的 Code Review 清单评审对象转换时必须逐项核对非 Converter 的生产文件不得使用 MapStructMapper或Mappers.getMapper。非 Converter 文件不得用反射拷贝、JSON 往返或ObjectMapper.convertValue做项目对象转换。非 Converter 文件不得定义返回 Chat2DB 项目对象的toXxx、fromXxx、convertXxx方法。Converter 文件不得依赖服务、存储组件、Mapper、Repository、实现类或 Spring Bean 查找。*Converter与*Convertor类型必须位于converter/convertor包中除非有明确的遗留例外。边界类不得出现可疑的new set映射序列。Converter 内部的反射拷贝、JSON 往返或ObjectMapper.convertValue必须有显式理由。同时契约也给出审慎条款接口方法、SQL 补全/解析器候选对象构造、SQL Builder 临时对象不自动视为违规需要结合上下文评审——这避免把规范误伤到合理的工具性代码上。9. 规范在仓库中的验证与延伸除了上述 Converter 实现外读者还可以从以下文件继续深入验证本契约的执行情况Web 层更多 Converter/api/converter/ai/ChatConverter.java、/api/converter/er/ErWebConverter.java、/api/converter/redis/RedisKeyConverter.java、/api/converter/driver/JdbcDriverConverter.java。插件层测试IGenericMetaDataConverterTest与MysqlSqlCompletionTokenCandidateConverterTest分别验证了 generic 插件与 mysql 插件的转换行为。相关规范文档java-module-boundaries.md模块边界、java-web-controller-contracts.mdWeb 控制器契约、java-interface-contracts.md接口契约。实践建议在 Chat2DB 仓库中新增一个跨层业务对象时遵循三步走——先确定目标层与源层按本文第 3 节归属表选择 Converter 所在模块再在converter包中新建XxxConverter或扩展既有 Converter声明Mapper(componentModel spring)抽象方法使用Mapping/Mappings处理字段名差异与ignore最后由业务代码委托调用严格避免在 Controller、Service、存储实现中手写任何new set或反射拷贝逻辑。配合第 8 节评审清单即可保证对象转换集中、可测试且不泄漏边界模型。【免费下载链接】Chat2DBChat2DB is a free, cross-platform, local-first database client and SQL workspace for developers, DBAs, analysts, and data teams. Connect to 40 databases, manage data, edit and run SQL, and use your own AI model to generate, explain, and optimize queries. Available on desktop, web, Docker, and CLI, with MCP support.项目地址: https://gitcode.com/GitHub_Trending/ch/Chat2DB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考