Skip to content

078. Battice 服务依赖与代码版本查找 ​

学习目标 ​

这一节继续梳理 Battice 服务内部关系,并补充如何查代码版本。

学完后,你应该能理解:

  • SQLModel、BatticeService 和配置驱动接口的区别;
  • 分页查询为什么是底层核心;
  • 单条查询、新增、批量新增如何复用分页查询;
  • 删除和更新的返回策略;
  • 如何通过 Git 历史定位课程对应代码。

三种数据访问形态 ​

项目里可以看到三种形态。

第一种是直接使用 SQLModel。

它面向对象,适合写具体业务逻辑。

第二种是使用 BatticeService。

它通过扫描实体类生成 modelConfig。

再把实体类包装成通用服务。

第三种是使用配置驱动的 Generic 路由。

它不扫描实体类。

而是从缓存或数据库里读取 modelConfig。

第二种和第三种底层都会调用同一批 Battice 服务函数。

modelConfig 的来源 ​

modelConfig 有两个来源。

如果有实体类,就从实体类扫描出来。

例如读取表名、字段名、字段类型。

如果没有实体类,就从模块配置表读取。

再通过缓存拿到配置。

两种方式只是配置来源不同。

后面的查询、新增、更新、删除逻辑可以共用。

分页查询是底层核心 ​

分页查询不依赖其他服务函数。

它是比较底层的能力。

流程大致是:

  1. 处理默认排序和内置筛选;
  2. 执行查询前置拦截器;
  3. 构建查询 SQL;
  4. 参数化执行 SQL;
  5. 转换结果值;
  6. 执行查询后置拦截器;
  7. 按需再查总数;
  8. 返回列表结果。

后面很多服务会复用它。

查数据和查总数 ​

分页查询里有两类任务。

查数据时,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 历史和提交对比;
  • 导入包时要按技术栈来源判断。

AI Agent 课程学习文档。