modify: 建表冲突wt

This commit is contained in:
2026-08-03 14:33:11 +08:00
parent baede32657
commit 1b7d46c805
2 changed files with 112 additions and 3 deletions
+89
View File
@@ -0,0 +1,89 @@
# chongming-database
异步 SQLite 数据库封装,基于 `aiosqlite` + `SQLModel`(SQLAlchemy),提供简洁的初始化与会话管理接口。
---
## 安装
```bash
uv pip install -e utils/database
```
---
## API
### `Database` 类
```python
from pathlib import Path
from chongming_database import Database
# 创建实例
db = Database(Path("data/storage/chongming/app.db"))
# 初始化(创建引擎 + 自动建表)
await db.initialize()
# 获取异步会话
async with db.get_session() as session:
# 使用 SQLModel / SQLAlchemy 进行查询
result = await session.execute(...)
...
# 关闭连接
await db.dispose()
```
### 方法说明
| 方法 | 签名 | 说明 |
| ------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `__init__` | `(db_path: Path)` | 传入 SQLite 文件路径,自动创建父目录 |
| `initialize` | `async () -> None` | 创建异步 SQLAlchemy 引擎(`sqlite+aiosqlite:///`),执行 `SQLModel.metadata.create_all` 自动建表,构建 `sessionmaker`(`expire_on_commit=False`) |
| `get_session` | `() -> AsyncSession` | 返回一个新的异步会话。如果 `initialize()` 未调用则抛出 `RuntimeError` |
| `dispose` | `async () -> None` | 关闭异步引擎并清除内部状态 |
---
## 使用示例
### 配合 SQLModel 定义表
```python
from sqlmodel import SQLModel, Field
from datetime import datetime, timezone
class User(SQLModel, table=True):
id: str = Field(primary_key=True)
username: str = Field(max_length=50, unique=True, index=True)
created_at: datetime = Field(
default_factory=lambda: datetime.now(timezone.utc)
)
```
### 完整生命周期
```python
from chongming_database import Database
from pathlib import Path
db = Database(Path("./data/myapp.db"))
await db.initialize()
try:
async with db.get_session() as session:
user = User(id="u1", username="john")
session.add(user)
await session.commit()
finally:
await db.dispose()
```
---
## 依赖
- `aiosqlite` — 异步 SQLite 驱动
- `sqlmodel` — SQLAlchemy + Pydantic 融合 ORM
+23 -3
View File
@@ -1,6 +1,7 @@
from pathlib import Path
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy.exc import OperationalError
from sqlmodel import SQLModel
class Database:
@@ -11,14 +12,33 @@ class Database:
self._db_path.parent.mkdir(parents=True, exist_ok=True)
async def initialize(self):
"""异步初始化,创建引擎并建立表"""
"""异步初始化,创建引擎并建立表
注意:DDL(CREATE TABLE)在 SQLite 中会自动提交,无需包裹在事务中。
使用 connect() 而非 begin() 可以避免事务失败状态导致的连锁异常。
checkfirst=True 会在建表前检查表是否已存在(通过 sqlite_master),
大幅减少多进程并发启动时的 "table already exists" 冲突。
即使 checkfirst 与实际 CREATE 之间存在竞态窗口,后续的 try/except
也会兜底捕获漏网的 OperationalError。
"""
self._engine = create_async_engine(
f"sqlite+aiosqlite:///{self._db_path}",
echo=False,
future=True,
)
async with self._engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
async with self._engine.connect() as conn:
try:
await conn.run_sync(SQLModel.metadata.create_all, checkfirst=True)
except OperationalError as e:
# 多进程并发启动时,checkfirst 与实际 CREATE 之间仍有极小的竞态窗口:
# 多个进程同时判断"表不存在"并执行 CREATE TABLE,
# 后到者会报 "table ... already exists"。此时表已由其他进程建好,忽略即可。
#
# 注意:使用 connect() 而非 begin(),DDL 在 SQLite 中自动提交,
# 无需手动 commit/rollback,避免事务失败状态的连锁异常。
if "already exists" not in str(e).lower():
raise
self._session_maker = sessionmaker(
self._engine, # type: ignore
class_=AsyncSession,