Files
konabot/docs/database.md

6.1 KiB
Raw Blame History

数据库系统使用文档

本文档详细介绍了本项目中使用的异步数据库系统,包括其架构设计、使用方法和最佳实践。

系统概述

本项目的数据库系统基于 aiosqlite 库构建,提供了异步的 SQLite 数据库访问接口。系统主要特性包括:

  1. 异步操作:完全支持异步/await模式适配NoneBot2框架
  2. 连接池:内置连接池机制,提高数据库访问性能
  3. 参数化查询支持安全的参数化查询防止SQL注入
  4. SQL文件支持可以直接执行SQL文件中的脚本
  5. 类型支持:支持 pathlib.Pathstr 类型的路径参数

核心类和方法

DatabaseManager 类

DatabaseManager 是数据库操作的核心类,提供了以下主要方法:

初始化

from konabot.common.database import DatabaseManager
from pathlib import Path

# 使用默认数据库路径
db = DatabaseManager()

# 指定了义数据库路径
db = DatabaseManager("./data/myapp.db")
db = DatabaseManager(Path("./data/myapp.db"))

查询操作

# 执行查询语句并返回结果
results = await db.query("SELECT * FROM users WHERE age > ?", (18,))

# 从SQL文件执行查询
results = await db.query_by_sql_file("./sql/get_users.sql", (18,))

执行操作

# 执行非查询语句
await db.execute("INSERT INTO users (name, email) VALUES (?, ?)", ("张三", "zhangsan@example.com"))

# 执行SQL脚本不带参数
await db.execute_script("""
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        email TEXT UNIQUE
    );
    INSERT INTO users (name, email) VALUES ('测试用户', 'test@example.com');
""")

# 从SQL文件执行非查询语句
await db.execute_by_sql_file("./sql/create_tables.sql")

# 带参数执行SQL文件
await db.execute_by_sql_file("./sql/insert_user.sql", ("张三", "zhangsan@example.com"))

# 执行多条语句(每条语句使用相同参数)
await db.execute_many("INSERT INTO users (name, email) VALUES (?, ?)", [
    ("张三", "zhangsan@example.com"),
    ("李四", "lisi@example.com"),
    ("王五", "wangwu@example.com")
])

# 从SQL文件执行多条语句每条语句使用相同参数
await db.execute_many_values_by_sql_file("./sql/batch_insert.sql", [
    ("张三", "zhangsan@example.com"),
    ("李四", "lisi@example.com")
])

SQL文件处理机制

单语句SQL文件

-- insert_user.sql
INSERT INTO users (name, email) VALUES (?, ?);
# 使用方式
await db.execute_by_sql_file("./sql/insert_user.sql", ("张三", "zhangsan@example.com"))

多语句SQL文件

-- setup.sql
CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT UNIQUE
);

CREATE TABLE IF NOT EXISTS profiles (
    user_id INTEGER,
    age INTEGER,
    FOREIGN KEY (user_id) REFERENCES users(id)
);
# 使用方式
await db.execute_by_sql_file("./sql/setup.sql")

多语句带不同参数的SQL文件

-- batch_operations.sql
INSERT INTO users (name, email) VALUES (?, ?);
INSERT INTO profiles (user_id, age) VALUES (?, ?);
# 使用方式
await db.execute_by_sql_file("./sql/batch_operations.sql", [
    ("张三", "zhangsan@example.com"),  # 第一条语句的参数
    (1, 25)  # 第二条语句的参数
])

最佳实践

1. 数据库表设计

-- 推荐的表设计实践
CREATE TABLE IF NOT EXISTS example_table (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

2. SQL文件组织

建议按照功能模块组织SQL文件

plugin/
├── sql/
│   ├── create_tables.sql
│   ├── insert_data.sql
│   ├── update_data.sql
│   └── query_data.sql
└── __init__.py

3. 错误处理

try:
    results = await db.query("SELECT * FROM users WHERE id = ?", (user_id,))
except Exception as e:
    logger.error(f"数据库查询失败: {e}")
    # 处理错误情况

4. 连接管理

# 在应用启动时初始化
db_manager = DatabaseManager()

# 在应用关闭时清理连接
async def shutdown():
    await db_manager.close_all_connections()

高级特性

连接池配置

class DatabaseManager:
    def __init__(self, db_path: Optional[Union[str, Path]] = None):
        # 连接池大小配置
        self._pool_size = 5  # 可根据需要调整

事务支持

# 通过execute方法的自动提交机制支持事务
await db.execute("BEGIN TRANSACTION")
try:
    await db.execute("INSERT INTO users (name) VALUES (?)", ("张三",))
    await db.execute("INSERT INTO profiles (user_id, age) VALUES (?, ?)", (1, 25))
    await db.execute("COMMIT")
except Exception:
    await db.execute("ROLLBACK")
    raise

注意事项

  1. 异步环境:所有数据库操作都必须在异步环境中执行
  2. 参数安全始终使用参数化查询避免SQL注入
  3. 资源管理:确保在应用关闭时调用 close_all_connections()
  4. SQL解析:使用 sqlparse 库准确解析SQL语句正确处理包含分号的字符串和注释
  5. 错误处理:适当处理数据库操作可能抛出的异常

常见问题

Q: 如何处理数据库约束错误?

A: 确保SQL语句中的字段名正确引用特别是保留字需要使用双引号包围

CREATE TABLE air_conditioner (
    id VARCHAR(128) PRIMARY KEY, 
    "on" BOOLEAN NOT NULL,  -- 使用双引号包围保留字
    temperature REAL NOT NULL
);

Q: 如何处理多个语句和参数的匹配?

A: 当SQL文件包含多个语句时参数应该是参数列表每个语句对应一个参数元组

await db.execute_by_sql_file("./sql/batch.sql", [
    ("参数1", "参数2"),  # 第一个语句的参数
    ("参数3", "参数4")   # 第二个语句的参数
])

通过遵循这些指南和最佳实践,您可以充分利用本项目的异步数据库系统,构建高性能、安全的数据库应用。