|
2 | 2 | title: 事务
|
3 | 3 | ---
|
4 | 4 |
|
5 |
| -## session 生成器 |
| 5 | +默认情况下,如果将数据库引擎参数 `echo` 设置为 True,你将会看到事务总是被开启,即便那是一个查询语句;但这并不是因为我们错误的使用了 |
| 6 | +SQLAlchemy,你可以查看 [#6921](https://github.com/sqlalchemy/sqlalchemy/discussions/6921) 了解详情 |
6 | 7 |
|
7 |
| -这是官方一种比较流行的方法,通过迭代 session 控制隔离,并通过上下文异常进行回滚,最后关闭 session |
| 8 | +::: details 简要总结 |
| 9 | +任何遵循 [PEP-429](https://peps.python.org/pep-0249) 进行设计的 Python 数据库连接器或 ORM,都将默认开启事务,你仅可以控制事务是否默认执行提交 |
8 | 10 |
|
9 |
| -这种方式应用于接口函数,通过依赖注入实现 session 传递,在 session 应用方面,它被认为是线程安全的 |
| 11 | +在 SQLAlchemy 中,你可以选择不使用它自身的事务模式,但这需要将数据库本身的事务隔离级别设置为 `AUTOCOMMIT` |
| 12 | +,详情请查看: [了解 DBAPI 级别的 Autocommit 隔离级别](https://docs.sqlalchemy.org.cn/en/20/core/connections.html#understanding-the-dbapi-level-autocommit-isolation-level) |
| 13 | +::: |
10 | 14 |
|
11 |
| -```py{3} |
| 15 | +## Session 生成器 |
| 16 | + |
| 17 | +这是一种类似于官方文档的使用方法,但这种方法并没有真正达到事务的目的,因为它不会自动执行提交,所以,你可以将它理解为仅适用于查询,否则,必须手动执行 |
| 18 | +`commit()` 方法 |
| 19 | + |
| 20 | +```python |
| 21 | +async def get_db(): |
| 22 | + """session 生成器""" |
| 23 | + async with async_db_session() as session: |
| 24 | + yield session |
| 25 | + |
| 26 | +# Session Annotated |
| 27 | +CurrentSession = Annotated[AsyncSession, Depends(get_db)] |
| 28 | +``` |
| 29 | + |
| 30 | +这种方法通常直接应用于接口函数,在 session 应用方面,它被认为是线程安全的 |
| 31 | + |
| 32 | +```python |
12 | 33 | @router.get('')
|
13 |
| -async def get_pagination_apis( |
14 |
| - db: CurrentSession |
15 |
| -) -> ResponseModel: |
16 |
| - ... |
| 34 | +async def get_pagination_apis(db: CurrentSession) -> ResponseModel: |
| 35 | + ... |
17 | 36 | ```
|
18 | 37 |
|
19 | 38 | ## `begin()`
|
20 | 39 |
|
21 |
| -这种方式由 SQLAlchemy 官方实现,相对第一种方式来说,更加原生,在线程安全方面,由于同一个接口中,可能存在多次调用,所以没有第一种方式更加严谨, |
22 |
| -不过呢,对于此架构来说,它更加符合架构风格 |
| 40 | +这种方式由 SQLAlchemy 官方实现,在线程安全方面,由于在同一个函数中,可能存在多次调用,所以没有 Session 生成器严谨 |
23 | 41 |
|
24 |
| -```py{2} |
| 42 | +```python |
| 43 | +# [!code word:begin] |
25 | 44 | async def create(*, obj: CreateIns) -> None:
|
26 | 45 | async with async_db_session.begin() as db:
|
27 | 46 | await xxx_dao.create(db, obj)
|
28 | 47 | ```
|
29 | 48 |
|
30 | 49 | ## 如何选择?
|
31 | 50 |
|
32 |
| -对于两种事务方法,我们更推荐第二种; |
| 51 | +以上两种方法,我们更推荐第二种; |
33 | 52 |
|
34 |
| -通常情况下,第一种方法我们更多用于分页查询接口,理论上来说,查询接口我们无需使用事务,但由于分页查询我们使用了第三方库,并且,为了尊重 |
35 |
| -fastapi 官方教程,我们仍将它保留 |
| 53 | +对于 FBA 来说,它更加符合架构风格,并且也能减少千篇一律的接口参数(纯作者强迫症) |
36 | 54 |
|
37 |
| -而第二种,对于无需使用事务的数据库操作,我们只需将 `begin()` 方法去掉,改为 `async with async_db_session() as db:` |
38 |
| -即可,这种方式更加灵活,并且也能减少千篇一律的接口参数(纯作者强迫症),同时,它也是 sqlalchemy 2.0+ 的官方推荐使用方法 |
| 55 | +而对于无需使用自动提交的事务,我们只需将 `begin()` 方法去掉,直接使用 `async_db_session()` 即可 |
0 commit comments