fix(database): 修复 detached 对象延迟加载导致的 greenlet 错误

问题:
- CRUD 返回的对象在 session 关闭后变为 detached 状态
- 访问属性时 SQLAlchemy 尝试延迟加载,但没有 session
- 导致: greenlet_spawn has not been called

根本原因:
- SQLAlchemy 对象在 session 外被访问
- 延迟加载机制尝试在非异步上下文中执行异步操作

修复方案:
1. CRUDBase.get_by(): 在 session 内预加载所有列
2. CRUDBase.get_multi(): 在 session 内预加载所有实例的所有列
3. PersonInfo.get_value(): 添加异常处理,防御性编程

影响:
- 所有通过 CRUD 获取的对象现在都完全加载
- 避免了 detached 对象的延迟加载问题
- 可能略微增加初始查询时间,但避免了运行时错误
This commit is contained in:
Windpicker-owo
2025-11-01 16:22:54 +08:00
parent e45e17f979
commit c98091938e
3 changed files with 36 additions and 13 deletions

View File

@@ -113,10 +113,19 @@ class CRUDBase:
result = await session.execute(stmt)
instance = result.scalar_one_or_none()
# 写入缓存
if instance is not None and use_cache:
cache = await get_cache()
await cache.set(cache_key, instance)
if instance is not None:
# 触发所有列的加载,避免 detached 后的延迟加载问题
# 遍历所有列属性以确保它们被加载到内存中
for column in self.model.__table__.columns:
try:
getattr(instance, column.name)
except Exception:
pass # 忽略访问错误
# 写入缓存
if use_cache:
cache = await get_cache()
await cache.set(cache_key, instance)
return instance
@@ -166,6 +175,14 @@ class CRUDBase:
result = await session.execute(stmt)
instances = result.scalars().all()
# 触发所有实例的列加载,避免 detached 后的延迟加载问题
for instance in instances:
for column in self.model.__table__.columns:
try:
getattr(instance, column.name)
except Exception:
pass # 忽略访问错误
# 写入缓存
if use_cache:
cache = await get_cache()