sessions.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. """SQLAlchemy 数据库连接与会话工具。
  2. Engine 管理数据库连接池;Session 表示一次数据库工作单元,负责查询和提交事务。
  3. """
  4. from collections.abc import Iterator
  5. from enum import StrEnum
  6. from sqlalchemy import URL, Engine, create_engine
  7. from sqlalchemy.orm import Session, sessionmaker
  8. from app.core.config import Settings
  9. class DatabaseName(StrEnum):
  10. """三个业务数据库的逻辑名称,使用枚举可避免到处手写字符串。"""
  11. CORE = "core"
  12. AGENT = "agent"
  13. ANALYTICS = "analytics"
  14. def build_mysql_url(settings: Settings, database: DatabaseName) -> URL:
  15. """根据配置和目标数据库生成 SQLAlchemy 连接地址。"""
  16. database_names = {
  17. DatabaseName.CORE: settings.mysql_core_database,
  18. DatabaseName.AGENT: settings.mysql_agent_database,
  19. DatabaseName.ANALYTICS: settings.mysql_analytics_database,
  20. }
  21. return URL.create(
  22. drivername="mysql+pymysql",
  23. username=settings.mysql_user,
  24. password=settings.mysql_password,
  25. host=settings.mysql_host,
  26. port=settings.mysql_port,
  27. database=database_names[database],
  28. query={"charset": "utf8mb4"},
  29. )
  30. def create_mysql_engine(settings: Settings, database: DatabaseName) -> Engine:
  31. """创建连接池;此时通常还不会立刻执行 SQL。"""
  32. return create_engine(
  33. build_mysql_url(settings, database),
  34. # 借出连接前先检查连接是否有效,避免拿到已被 MySQL 关闭的旧连接。
  35. pool_pre_ping=True,
  36. # 定期回收连接,降低 MySQL 服务端超时导致断连的概率。
  37. pool_recycle=1800,
  38. )
  39. def create_session_factory(engine: Engine) -> sessionmaker[Session]:
  40. """创建 Session 工厂;调用这个工厂才能得到具体会话。"""
  41. return sessionmaker(bind=engine, expire_on_commit=False)
  42. def session_scope(factory: sessionmaker[Session]) -> Iterator[Session]:
  43. """提供自动提交/回滚的会话上下文,yield 把会话交给调用方使用。"""
  44. with factory.begin() as session:
  45. yield session