9.9 KiB
SQLite3 数据库操作模块技术文档
概述
该模块提供了一个轻量级的 SQLite3 数据库封装类 SQLite3,支持多线程安全访问、字符编码转换(本地化与 UTF-8)、自动数据库路径管理,并通过 Record 类将查询结果封装为对象。模块还集成了日志记录和公共数据存储机制。
主要功能包括:
- 自动连接与重连数据库
- 多线程隔离属性管理
- SQL 执行与结果遍历
- 表结构查询(表名、字段)
- 事务控制(提交、回滚)
- 字符串编码处理(str ↔ unicode)
- 数据库路径自动生成与缓存
模块依赖
import os, sys
import thread
from sqlite3 import dbapi2 as sqlite
import time
from localefunc import *
from folderUtils import mkdir
from PublicData import public_data
from mylog import mylog
第三方/内部模块说明:
| 模块 | 功能 |
|---|---|
localefunc |
提供 localeString() 和 local_encoding 变量,用于本地字符串处理 |
folderUtils.mkdir |
确保目录存在(创建目录) |
PublicData.public_data |
全局共享数据容器,用于缓存数据库实例和路径 |
mylog |
日志输出函数 |
公共函数
logit(s)
记录当前文件名及消息到日志系统。
参数:
s(str) - 要记录的日志内容
示例:
logit("初始化数据库")
# 输出: __file__: 初始化数据库
str2unicode(s)
将字符串转换为 Unicode,尝试使用本地编码或 UTF-8 解码。
参数:
s(str 或其他类型) - 输入字符串
返回值:
- 成功时返回
unicode对象 - 若解码失败,则返回
buffer(s) - 非字符串类型原样返回
编码优先级:
local_encoding(来自localefunc)'utf8'- fallback 到
buffer
unicode2str(s)
将 Unicode 或其他类型转换为适合输出的字符串形式。
参数:
s- 待转换的数据
返回值:
| 类型 | 转换方式 |
|---|---|
int / long |
返回 long(s) |
buffer |
转为 str(s) |
unicode |
.encode('utf8') |
| 其他 | 原样返回 |
argConvert(args)
递归地将参数中的字符串转为 Unicode,支持多种数据结构。
参数:
args- 可为tuple,list,dict, 或单个值
行为:
- 列表或元组 → 对每个元素调用
str2unicode - 字典 → 对每个值调用
str2unicode - None → 返回 None
- 其他 → 直接返回
用途: 在执行 SQL 前统一编码格式
核心类:Record
表示一条数据库记录,字段作为属性访问。
构造函数:__init__(self, data, localize=False)
参数:
data(dict) - 键值对数据(如从数据库取出)localize(bool) - 是否对字符串字段应用localeString()
逻辑:
- 所有键名转小写后设为对象属性
- 若
localize=True且值是字符串,则调用localeString()
示例:
r = Record({'Name': '张三', 'Age': 25}, localize=True)
print(r.name) # 输出经过 localeString 处理后的名字
print(r.age) # 25
__getattr__(self, name)
实现属性访问时统一转为小写。
⚠️ 注意:此方法存在无限递归风险!
实现中直接调用了
getattr(self, name)而不是访问__dict__,会导致死循环。✅ 正确做法应为:
try: return self.__dict__[name.lower()] except KeyError: raise AttributeError(name)
__str__(self)
返回对象所有属性的格式化字符串。
返回格式:
[field1 : value1
field2 : value2]
核心类:SQLite3
封装了 SQLite3 的连接、执行、查询等操作。
构造函数:__init__(self, dbpath, localize=False)
参数:
dbpath(str) - 数据库文件路径localize(bool) - 查询结果是否进行本地化处理(传给Record)
初始化动作:
- 创建线程隔离映射
threadMap - 调用
_connection(dbpath)建立连接
私有方法:_connection(dbpath=None)
重新建立数据库连接。
参数:
dbpath- 新的数据库路径(可选)
行为:
- 更新
self.dbpath - 创建新的
sqlite.Connection和cursor - 重置
result,sqlcmd
特殊方法:__setattr__ 和 __getattr__
实现线程局部属性存储,确保不同线程不会互相干扰属性读写。
工作原理:
- 使用
thread.get_ident()获取当前线程 ID - 每个线程在
self.threadMap[thread_id]中拥有独立命名空间 - 属性读写仅影响当前线程
✅ 这是一种模拟“线程局部变量”的方式,避免并发冲突
方法:tables()
获取数据库中所有用户表名。
SQL 查询:
SELECT * FROM sqlite_master WHERE type='table'
返回值:
listof table names (str)
流程:
- 执行查询
- 遍历结果,提取
.name字段 - 返回表名列表
方法:columns(tablename)
⚠️ 存在拼写错误:形参名为 tablenmae,但使用了 tablename
应修正为:
def columns(self, tablename):
self.SQL('select * from %s' % tablename)
self.desc = self.results.description
return self.desc
功能:
- 查询指定表的一条记录以获取字段描述
- 返回
cursor.description(包含字段名、类型等信息)
方法:FETCHALL()
执行 fetchall() 并返回全部结果。
❌ 当前实现有问题:
r = True
r = self.cursor.fetchall()
return r
第一行赋值无意义,建议改为:
def FETCHALL(self):
if self.results:
return self.cursor.fetchall()
return []
方法:_eatCursorNext()
清空游标中未消费的结果,防止 ProgrammingError。
目的:
- 避免多次调用
next()导致异常 - 在新查询前清理旧状态
注意: 使用 .next() 是旧式迭代器接口(Python 2),现代环境建议使用 fetchone() 或检查一致性。
方法:SQL(cmd, args=(), retry=0)
执行一条或多条 SQL 语句。
参数:
cmd(str) - SQL 语句args(tuple/list/dict) - 参数化查询参数retry(int) - 重试次数(未实际使用)
逻辑:
- 若连接断开,自动重连并重试
- 清理上一次游标残留
- 转换参数编码(
argConvert) - 如果含多个语句(
;分隔),使用executescript - 否则使用
execute(cmd, args) - 记录
lastSQL和description
异常处理:
- 打印错误信息
- 抛出原始异常
方法:FETCH()
逐行获取查询结果,每行封装为 Record 对象。
返回值:
- 成功 →
Record实例 - 结束 →
None - 出错 → 抛出异常
流程:
- 检查是否有结果集
- 获取
description(字段元信息) - 调用
.next()获取下一行 - 映射字段名 → 值,并用
unicode2str转码 - 构造
Record(data, self.localize)
方法:COMMIT()
提交事务,并设置大小写敏感 LIKE 匹配。
执行命令:
PRAGMA case_sensitive_like = 1;
✅ 注意:即使没有显式 BEGIN,SQLite 默认自动提交模式下也需 COMMIT 显式结束事务
此外还会尝试 fetchall() 清空结果,避免挂起。
方法:ROLLBACK()
执行回滚操作。
self.SQL('ROLLBACK')
方法:BEGIN()
预留方法,目前为空。
⚠️ 注释掉
self.SQL('BEGIN'),可能导致事务未正确开启
✅ 建议启用:
def BEGIN(self):
self.SQL('BEGIN')
方法:CLOSE()
关闭数据库连接。
动作:
- 设置
self.con = None - 设置
self.cursor = None
⚠️ 不会自动提交或回滚,调用前请手动处理事务
辅助函数:getDataBase(name)
根据名称获取或创建一个全局唯一的数据库连接实例。
参数:
name(str) - 数据库标识名(如'main','user')
工作流程:
- 查找缓存
public_data['db_main'] - 若不存在:
- 查找路径
dbpath_main - 若仍不存在:
- 使用
ProgramPath+var/xxx.db3自动生成路径 - 创建目录(
mkdir(var)) - 缓存路径
- 使用
- 实例化
SQLite3(dbpath) - 缓存数据库实例
- 查找路径
- 检查连接有效性,必要时重连
- 返回数据库对象
优势:
- 单例模式,避免重复打开
- 自动路径管理
- 支持热修复断连
使用示例
# 获取数据库实例
db = getDataBase('test')
# 查询表
db.SQL("SELECT * FROM users WHERE age > ?", (18,))
while True:
row = db.FETCH()
if not row:
break
print(row.name, row.age)
# 插入数据(事务)
db.BEGIN()
db.SQL("INSERT INTO users(name,age) VALUES (?,?)", ("Alice", 25))
db.COMMIT()
# 获取所有表
tables = db.tables()
print(tables)
已知问题与改进建议
| 问题 | 描述 | 建议修复 |
|---|---|---|
__getattr__ 死循环 |
调用自身导致无限递归 | 改为访问 self.__dict__ |
columns() 拼写错误 |
参数名 tablenmae ≠ 使用 tablename |
更正拼写 |
FETCHALL() 逻辑冗余 |
初始赋值无效 | 简化代码 |
BEGIN() 为空 |
无法开启事务 | 启用 self.SQL('BEGIN') |
| 异常捕获过于宽泛 | except: 隐藏错误 |
改为具体异常类型 |
| Python 2 风格 | 使用 thread, next() |
迁移到 threading, fetchone() |
总结
本模块是一个适用于中小型项目的 SQLite 封装工具,具备以下优点:
✅ 自动路径生成与缓存
✅ 多线程安全属性隔离
✅ 编码自动转换(兼容中文)
✅ 简洁的面向对象查询接口
⚠️ 存在若干 bug 和设计瑕疵,建议在正式项目中审慎使用,并优先修复上述问题。
最后更新时间: {{ 自动生成时间 }}
作者: 自动生成文档 by AI
适用版本: Python 2.x(因使用 thread 和 next())