# 数据库会话 :::tip 说明 项目通过 Flask 的 `g` 对象和 `before_request` / `after_request` 钩子实现请求级数据库会话管理,写操作按业务成功与否自动 commit/rollback。核心代码位于 `src/core/database.py` 和 `src/middleware/transaction.py`。 ::: ## 核心架构 数据库会话管理位于 `src/core/database.py` 和 `src/middleware/transaction.py`。 ### SQLAlchemy + Flask-SQLAlchemy 项目使用 Flask-SQLAlchemy 扩展,通过 `db` 对象管理数据库会话: ```python # src/core/database.py from flask_sqlalchemy import SQLAlchemy # 创建 Flask-SQLAlchemy 实例 db = SQLAlchemy() ``` 在 `src/app.py` 的 `create_app()` 中初始化: ```python def create_app(): app = Flask(__name__) # 配置数据库 URI app.config['SQLALCHEMY_DATABASE_URI'] = DB_URL # 初始化 SQLAlchemy db.init_app(app) return app ``` 业务代码通过 `db.session` 获取请求级会话: ```python from src.core.database import db # 添加记录 db.session.add(obj) db.session.flush() # 发送 SQL,获取自增 ID,不提交 # 查询 result = db.session.query(Model).filter(...).all() ``` ### Flask g 对象 Flask 的 `g` 对象是请求级别的全局变量,每个请求都有独立的 `g` 对象: ```python from flask import g # 在请求开始时设置 g.user_id = 1 # 在请求处理过程中获取 user_id = g.user_id ``` ## 会话中间件 `src/middleware/transaction.py` 中的中间件负责会话的创建、提交和关闭: ```python from flask import request, g from src.core.database import db def register_transaction(app): """注册 AOP 事务管理中间件""" @app.before_request def _transaction_begin(): """请求开始前:标记事务活跃""" g.transaction_active = True @app.after_request def _transaction_commit(response): """请求结束后:根据响应状态决定提交或回滚""" if request.method in ('POST', 'PUT', 'DELETE', 'PATCH'): if response.status_code == 200: try: body = response.get_json() if body and body.get('code') == 0: # 业务成功:提交事务 db.session.commit() else: # 业务失败:回滚事务 db.session.rollback() except Exception: db.session.rollback() else: # HTTP 非 200:回滚事务 db.session.rollback() return response @app.teardown_request def _transaction_rollback(exception): """请求异常时:回滚事务""" if exception: db.session.rollback() # 移除会话 db.session.remove() ``` ### 业务成功判断 中间件通过响应体 JSON 的 `code` 字段判断业务是否成功: ```python def _is_business_success(body: dict) -> bool: """仅当响应体为 JSON 且业务码 code == 0 视为成功""" if not body: return False if not isinstance(body, dict): return False return body.get('code') == 0 ``` ## 独立提交 `commit_independently()` 函数用于审计日志等需要无论业务成功与否都持久化的场景: ```python from src.core.database import db def commit_independently(obj): """ 使用独立session提交对象,不受请求事务影响 日志等审计数据无论业务成功或失败都应持久化, 因此需要绕过 AOP 事务中间件的 commit/rollback 机制。 """ independent_session = db.session_factory() try: independent_session.add(obj) independent_session.commit() except Exception: independent_session.rollback() raise finally: independent_session.close() ``` :::warning 使用场景 `commit_independently()` 创建独立会话,不受请求级事务影响。适用于操作日志、登录日志等审计数据的持久化。普通业务数据应使用 `db.session` 获取请求级会话,由中间件统一管理事务。 ::: ## 会话生命周期 ``` 请求进入 → before_request 标记事务活跃 → 业务代码通过 db.session 获取会话 → after_request 根据响应码 commit/rollback → teardown_request 异常时 rollback 并移除会话 ``` ## 中间件注册顺序 在 `src/app.py` 的 `create_app()` 中,按以下顺序初始化: ```python def create_app(): app = Flask(__name__) # 1. 加载配置(数据库、Redis 等) app.config.from_object(DatabaseConfig) # 2. 配置 CORS 跨域 CORS(app, ...) # 3. 可信代理配置(ProxyFix) app.wsgi_app = ProxyFix(app.wsgi_app, ...) # 4. 注册操作日志中间件(须在事务拦截器之前) register_operation_log(app) # 5. 注册 AOP 事务拦截器 register_transaction(app) # 6. 注册 API v1 路由 register_v1(app) # 7. 初始化扩展(数据库、Redis、调度器等) register_extends(app) # 8. 注册中间件(登录校验、演示模式、异常处理) register_middleware(app) # 9. 注入依赖(权限提供方、操作日志写入器) _inject_dependencies(app) return app ``` `register_operation_log` 在 `register_transaction` 之前,确保操作日志在事务提交前记录。 ## BaseDB.save() 方法 `BaseDB` 基类提供的 `save()` 方法只执行 `flush`,不执行 `commit`: ```python from src.core.database import db from src.common.base_model import BaseModel class BaseDB: def save(self): """保存模型到数据库(仅 flush,不 commit)""" db.session.add(self) db.session.flush() return self def delete(self): """软删除模型(仅 flush,不 commit)""" self.is_delete = 1 db.session.flush() ``` `flush` 的作用: 1. 将 SQL 语句发送到数据库执行 2. 获取自增 ID 等数据库生成的值 3. 但不提交事务,事务由中间件统一管理 ## 注意事项 :::warning 重要提示 1. **不要手动调用 `db.session.commit()`**:事务由中间件统一管理,手动 commit 可能导致事务提前提交 2. **不要手动调用 `db.session.rollback()`**:除非在 `try-except` 块中需要手动回滚 3. **只读操作使用 GET 方法**:GET 请求不会触发事务处理 4. **写操作必须返回 R 对象**:中间件通过 `code` 字段判断是否需要提交 ::: ## 总结 数据库会话管理通过 Flask 的 `g` 对象和 `before_request` / `after_request` 钩子实现请求级隔离,中间件自动根据业务响应码 commit/rollback,`commit_independently()` 提供独立事务能力用于审计数据持久化。这种设计确保了事务边界清晰,业务代码无需手动管理事务。