切换日光/暗黑模式
078. Battice 服务依赖与代码版本查找
学习目标
这一节继续梳理 Battice 服务内部关系,并补充如何查代码版本。
学完后,你应该能理解:
- SQLModel、
BatticeService和配置驱动接口的区别; - 分页查询为什么是底层核心;
- 单条查询、新增、批量新增如何复用分页查询;
- 删除和更新的返回策略;
- 如何通过 Git 历史定位课程对应代码。
三种数据访问形态
项目里可以看到三种形态。
第一种是直接使用 SQLModel。
它面向对象,适合写具体业务逻辑。
第二种是使用 BatticeService。
它通过扫描实体类生成 modelConfig。
再把实体类包装成通用服务。
第三种是使用配置驱动的 Generic 路由。
它不扫描实体类。
而是从缓存或数据库里读取 modelConfig。
第二种和第三种底层都会调用同一批 Battice 服务函数。
modelConfig 的来源
modelConfig 有两个来源。
如果有实体类,就从实体类扫描出来。
例如读取表名、字段名、字段类型。
如果没有实体类,就从模块配置表读取。
再通过缓存拿到配置。
两种方式只是配置来源不同。
后面的查询、新增、更新、删除逻辑可以共用。
分页查询是底层核心
分页查询不依赖其他服务函数。
它是比较底层的能力。
流程大致是:
- 处理默认排序和内置筛选;
- 执行查询前置拦截器;
- 构建查询 SQL;
- 参数化执行 SQL;
- 转换结果值;
- 执行查询后置拦截器;
- 按需再查总数;
- 返回列表结果。
后面很多服务会复用它。
查数据和查总数
分页查询里有两类任务。
查数据时,SQL 会选择需要返回的字段。
查总数时,SQL 只需要 COUNT。
如果接口同时需要数据和总数,就要执行两次查询。
第一次查当前页数据。
第二次把请求参数复制一份,设置 onlyCount = true,再查总数。
这是多数语言和框架都绕不开的事情。
单条查询复用分页查询
单条查询会把简单条件转成分页查询条件。
例如:
py
{"id": "001"}会变成:
txt
id = 001然后调用分页查询,只取一条数据。
这样单条查询可以复用:
- 字段转换;
- 查询构建;
- 前后置拦截器;
- 默认筛选;
- 结果格式化。
新增复用单条查询
新增数据时,先做字段转换。
例如前端传数组,后端可能要转成数据库字符串。
然后执行新增前置拦截器。
再构建 INSERT SQL 并执行。
如果自动提交事务,新增完成后会用单条查询把刚插入的数据查出来。
再执行新增后置拦截器。
最后把完整数据返回。
如果不自动提交事务,数据还没提交,查不出来。
这时只能返回影响行数。
批量新增复用分页查询
批量新增和单条新增类似。
区别是数据是一组。
批量新增完成后,会收集本次新增的 ID。
再用 IN 查询把这些数据一次查回来。
所以批量新增会复用分页查询。
只是筛选条件变成:
sql
id IN (...)更新和删除
更新流程和新增类似。
只是拦截器动作变成更新前、更新后。
更新后如果自动提交,也可以再查一遍最新数据返回。
删除不同。
删除后数据已经不存在。
所以删除通常返回影响行数。
如果没有自动提交事务,后置删除拦截器也不会执行。
因为外层事务可能还会回滚。
服务函数依赖关系
可以这样记:
- 分页查询是基础;
- 单条查询依赖分页查询;
- 新增依赖单条查询;
- 批量新增依赖分页查询;
- 更新通常依赖单条查询返回最新数据;
- 删除主要返回影响行数。
这个关系有助于调试。
如果底层分页查询有问题,上层很多服务都会受影响。
文档代码和实操代码
课程里的文档代码和实际写出来的代码可能不完全一模一样。
原因是实现过程中会有调整。
重点不是逐字符一致。
而是功能、结构和思路一致。
如果需要找到某个文件在某一节对应的版本,要使用 Git 历史。
查看单文件历史
可以在 IDE 里查看文件的 Git 历史。
常见信息包括:
- 这个文件改过几次;
- 每次提交说明;
- 每次改了哪些行;
- 哪一行最后由哪个提交修改。
这能帮助你把课件片段和代码版本对应起来。
对比提交
Git 可以对比任意两次提交。
你可以看到:
- 新增了哪些文件;
- 修改了哪些文件;
- 删除了哪些文件;
- 每个文件具体改了哪些行。
如果只知道某天讲了什么,也可以按日期附近的提交去找。
这样比在整个项目里盲找更快。
导入来源规律
Python 项目里经常有同名或相似对象。
可以先按来源分类。
例如:
- SQLModel 相关优先从
sqlmodel找; - 请求、响应、路由相关优先从
fastapi找; Request等底层对象可能来自starlette;- 类型注解相关通常来自
typing。
FastAPI 底层基于 Starlette。
SQLModel 底层基于 SQLAlchemy。
所以有些对象会从更底层的包里导入。
LangChain 相关更复杂
LangChain 生态里包更多。
可能有核心包、社区包、扩展包,而且有重名对象。
后面进入 LangChain 时,要更注意导入来源。
不要只看类名相同就随便导入。
这一节的重点
这一节把 Battice 服务函数的依赖关系讲完整。
你需要记住:
- SQLModel 适合具体业务;
BatticeService从实体类生成配置;- Generic 路由从缓存或数据库拿配置;
- 分页查询是最底层的核心服务;
- 单条查询、新增、批量新增会复用分页查询或单条查询;
- 文档代码和实操代码不必逐字符一致;
- 找对应版本要看 Git 历史和提交对比;
- 导入包时要按技术栈来源判断。