from contextlib import asynccontextmanager
from typing import AsyncIterator
from fastapi import Depends, FastAPI
from sqlalchemy import BigInteger, Column, String, text
from sqlalchemy.engine import URL
from sqlalchemy.ext.asyncio import (
AsyncAttrs,
AsyncConnection,
AsyncSession,
async_sessionmaker,
create_async_engine,
)
from sqlalchemy.orm import DeclarativeBase
################################################################################# 首先准备数据模型 ################################################################
class Base(AsyncAttrs, DeclarativeBase):
"""
ORM对象基类
"""
pass
class User(Base):
"""
用户表
"""
__tablename__ = "user"
id = Column(BigInteger, primary_key=True, autoincrement=True)
name = Column(String(20), nullable=False)
################################################################################# 创建数据库引擎 ################################################################
# 创建连接uri
db_uri = URL.create(
"mysql+asyncmy",
username="root",
password="123456",
host="localhost",
port=3306,
database="one",
)
# 指定连接参数
connect_args = {
"pool_size": 10, # 连接池大小,保持10个空闲连接
"max_overflow": 20, # 最大溢出连接数,当连接池耗尽时最多创建20个额外连接
"pool_timeout": 30, # 连接超时时间,从连接池获取连接的等待时间(秒)
"pool_recycle": 3600, # 连接回收时间,1小时后自动回收连接防止数据库超时
"pool_pre_ping": True, # 连接前检测,确保从池中获取的连接是有效的
"echo": True, # 打印sql语句,生产环境设置为False
}
# 创建mysql引擎
mysql_engine = create_async_engine(
db_uri,
**connect_args,
)
################################################################################# 创建数据库和表方法 ################################################################
async def create_db_and_tables():
"""
根据数据模型创建数据库和表
注意: 只负责创建,当数据模型字段的数据类型变更后,是不会自动修改表中对应列的数据类型的
"""
async with mysql_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
################################################################################# 绑定fastapi启动 ################################################################
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
fastapi服务上下文管理
"""
# 启动逻辑
await create_db_and_tables()
yield
# 关闭逻辑
# 创建fastapi服务实例
app = FastAPI(lifespan=lifespan)
################################################################################# 准备可以和fastapi Depends关联的工厂方法 ################################################################
# 会话实例工厂
session_factory = async_sessionmaker(
bind=mysql_engine,
class_=AsyncSession,
expire_on_commit=False, # 提交后不使对象过期,避免重复查询
autoflush=False, # 关闭自动刷新,手动控制刷新时机
)
async def get_conn() -> AsyncIterator[AsyncConnection]:
"""
异步生成异步连接,用于执行原生SQL,不推荐使用
结合Depends使用会在使用完后自动关闭连接
"""
async with mysql_engine.connect() as conn:
yield conn
async def get_session() -> AsyncIterator[AsyncSession]:
"""
异步生成异步会话实例,用于执行orm操作, 推荐使用
结合Depends使用会在使用完后自动关闭会话
"""
async with session_factory() as session:
yield session
################################################################################# 路由中使用会话/连接 ################################################################
@app.get("/add_user1")
async def add_user1(*, session: AsyncSession = Depends(get_session)):
"""
通过会话操作演示
"""
user = User(name="cjtarrr1")
async with session.begin():
# 开启自动提交/回滚的事务
session.add(user)
return {"id": user.id, "name": user.name}
@app.get("/add_user2")
async def add_user2(*, conn: AsyncConnection = Depends(get_conn)):
"""
通过底层连接操作演示
"""
async with conn.begin(): # 自动事务管理
query = text("INSERT INTO user(name) VALUES(:name)")
result = await conn.execute(query, {"name": "cjtarrr2"})
inserted_id = result.lastrowid # MySQL获取插入ID的方式
# 退出上下文时自动提交,出错时自动回滚
return {"id": inserted_id, "name": "cjtarrr2"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=10002)