Python SQLite:连接到数据库深度解析
目录
- 引言:数据库连接的重要性
sqlite3.connect()函数- 2.1 函数签名及参数
- 2.2
database参数:文件路径与内存数据库 - 2.3
timeout参数:锁定等待时间 - 2.4
isolation_level参数:事务隔离级别 - 2.5
detect_types参数:数据类型转换 - 2.6
check_same_thread参数:线程安全 - 2.7
factory和cached_statements参数
- 建立连接:代码示例
- 3.1 连接到文件数据库 (创建或打开)
- 3.2 连接到内存数据库
Connection对象- 4.1
Connection对象的功能 - 4.2
cursor()方法 - 4.3
commit()和rollback()方法 - 4.4
close()方法
- 4.1
- 关闭数据库连接
- 5.1 手动关闭:
conn.close() - 5.2 自动关闭:使用
with语句 (推荐)
- 5.1 手动关闭:
- 连接过程中的错误处理
- 6.1 常见错误类型
- 6.2 错误处理示例
- 最佳实践与注意事项
- 7.1 始终使用
with语句 - 7.2 理解
check_same_thread - 7.3 谨慎设置
isolation_level - 7.4 正确处理文件路径
- 7.1 始终使用
- 总结
1. 引言:数据库连接的重要性
在任何应用程序中,与数据库交互的第一步都是建立一个连接。这个连接是应用程序和数据库之间通信的桥梁。对于 SQLite 这种基于文件的数据库,连接操作意味着找到或创建一个数据库文件,并准备好读写数据。在 Python 中,sqlite3 模块提供了简洁而强大的 connect() 函数来完成这一任务。理解如何正确、高效且安全地建立和管理数据库连接,是进行任何 SQLite 数据库操作的基础。
2. sqlite3.connect() 函数
sqlite3.connect() 是 sqlite3 模块中用于建立数据库连接的核心函数。
2.1 函数签名及参数
sqlite3.connect(database, timeout=5.0, isolation_level='DEFERRED',
detect_types=0, check_same_thread=True, factory=sqlite3.Connection,
cached_statements=128, uri=False)
下面将详细解释其中最重要的几个参数。
2.2 database 参数:文件路径与内存数据库
这是 connect() 函数的第一个也是最重要的参数,它指定了要连接的数据库。
- 文件路径: 传入一个字符串,表示数据库文件的路径。
- 如果该路径下的文件不存在,
sqlite3会自动创建一个新的数据库文件。 - 如果文件存在,则会打开该数据库。
- 示例:
sqlite3.connect('my_database.db')
- 如果该路径下的文件不存在,
- 内存数据库: 传入特殊字符串
':memory:'。- 这会创建一个完全在 RAM 中的临时数据库。
- 所有数据在程序关闭或连接断开时都会丢失。
- 非常适合测试、临时数据存储或不需要持久化的场景。
- 示例:
sqlite3.connect(':memory:')
- URI 模式: 如果
uri=True,database参数可以是一个 URI 字符串,允许更复杂的连接选项。- 示例:
sqlite3.connect('file:path/to/my.db?mode=ro', uri=True)(只读模式)
- 示例:
2.3 timeout 参数:锁定等待时间
SQLite 数据库在写入操作时可能会对数据库文件进行锁定。timeout 参数(默认为 5.0 秒)指定了当数据库被锁定时代等待的时间。如果在这个时间内未能获取到锁,则会抛出 sqlite3.OperationalError 异常。
- 类型: 浮点数,表示秒数。
- 作用: 防止长时间阻塞,提高应用程序的响应性。
- 示例:
sqlite3.connect('my_database.db', timeout=10.0)
2.4 isolation_level 参数:事务隔离级别
此参数控制事务的自动提交行为。
'DEFERRED'(默认): 事务开始于第一次写入操作。'IMMEDIATE': 事务开始于BEGIN语句,即在第一次读写操作之前就尝试获取写锁。'EXCLUSIVE': 事务开始于BEGIN EXCLUSIVE语句,在第一次读写操作之前就获取排他写锁,阻止其他连接的读写。None: 禁用事务管理。每次execute()都会自动提交。通常不推荐,因为它放弃了 ACID 属性,可能导致数据不一致。- 示例:
sqlite3.connect('my_database.db', isolation_level='IMMEDIATE')
2.5 detect_types 参数:数据类型转换
此参数用于控制 SQLite 如何将数据库中的数据类型转换为 Python 数据类型。
0(默认): 不进行类型转换,数据以字符串、字节或数字形式返回。sqlite3.PARSE_DECLTYPES: 根据列的声明类型进行转换。例如,如果列声明为TIMESTAMP,它将尝试转换为 Python 的datetime对象。sqlite3.PARSE_COLNAMES: 根据列名中的类型提示进行转换。sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES: 结合两种模式。- 示例:
sqlite3.connect('my_database.db', detect_types=sqlite3.PARSE_DECLTYPES)
2.6 check_same_thread 参数:线程安全
此参数用于控制数据库连接是否可以在多个线程之间共享。
True(默认): 强制连接只能在创建它的线程中使用。如果在另一个线程中尝试使用,会抛出sqlite3.ProgrammingError。这是为了防止潜在的并发问题和数据损坏。False: 允许在多个线程之间共享连接。请注意,这需要您自行确保线程安全,例如使用锁来保护数据库操作。 如果没有适当的同步,可能导致数据损坏或不可预测的行为。- 示例:
sqlite3.connect('my_database.db', check_same_thread=False)(慎用,需要手动处理同步)
2.7 factory 和 cached_statements 参数
factory: 允许您指定一个自定义的Connection类。默认是sqlite3.Connection。cached_statements: 缓存的 SQL 语句数量,默认为 128。可以提高重复执行相同 SQL 语句的性能。
3. 建立连接:代码示例
3.1 连接到文件数据库 (创建或打开)
import sqlite3
import os
db_file = 'my_first_db.db'
# 检查文件是否存在
if os.path.exists(db_file):
print(f"数据库文件 '{db_file}' 已存在。")
else:
print(f"数据库文件 '{db_file}' 不存在,将自动创建。")
# 连接到数据库
try:
conn = sqlite3.connect(db_file)
print(f"成功连接到数据库: '{db_file}'")
# 可以进行一些操作,例如创建一个简单的表
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL
)
''')
conn.commit()
print("表 'messages' 创建成功或已存在。")
except sqlite3.Error as e:
print(f"数据库连接或操作失败: {e}")
finally:
if conn:
conn.close()
print("数据库连接已关闭。")
# 再次尝试连接,这次文件应该已经存在
print("\n再次尝试连接...")
conn_existing = sqlite3.connect(db_file)
print(f"再次成功连接到数据库: '{db_file}'")
conn_existing.close()
3.2 连接到内存数据库
import sqlite3
# 连接到内存数据库
try:
conn_in_memory = sqlite3.connect(':memory:')
print("成功连接到内存数据库。")
cursor = conn_in_memory.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS temp_data (
key TEXT PRIMARY KEY,
value TEXT
)
''')
cursor.execute("INSERT INTO temp_data (key, value) VALUES ('name', 'Alice')")
conn_in_memory.commit()
print("内存数据库中表 'temp_data' 创建并插入数据成功。")
cursor.execute("SELECT * FROM temp_data")
print(f"查询内存数据库结果: {cursor.fetchone()}")
except sqlite3.Error as e:
print(f"内存数据库连接或操作失败: {e}")
finally:
if conn_in_memory:
conn_in_memory.close()
print("内存数据库连接已关闭。")
# 再次连接内存数据库,数据会丢失
print("\n再次连接内存数据库,数据将丢失...")
conn_new_memory = sqlite3.connect(':memory:')
cursor_new = conn_new_memory.cursor()
cursor_new.execute("SELECT * FROM temp_data")
print(f"新的内存数据库中查询结果: {cursor_new.fetchone()}") # 预期为 None
conn_new_memory.close()
4. Connection 对象
sqlite3.connect() 函数成功执行后,会返回一个 Connection 对象。这个对象代表了应用程序与 SQLite 数据库之间的活动连接,是所有数据库操作的起点。
4.1 Connection 对象的功能
- 创建游标: 通过
cursor()方法创建Cursor对象。 - 事务管理: 通过
commit()和rollback()方法控制事务。 - 关闭连接: 通过
close()方法断开连接。 - 配置: 允许设置
row_factory等属性,影响查询结果的返回方式。
4.2 cursor() 方法
Connection 对象的 cursor() 方法用于创建 Cursor 对象。Cursor 对象负责执行 SQL 语句并获取结果。一个连接可以有多个游标。
conn = sqlite3.connect('example.db')
cursor = conn.cursor() # 创建一个游标
# ... 使用 cursor 执行 SQL ...
conn.close()
4.3 commit() 和 rollback() 方法
当您执行 INSERT, UPDATE, DELETE 等修改数据库的操作时,这些更改首先是在事务中进行的,并不会立即永久写入数据库文件。
conn.commit(): 将当前事务中的所有更改永久保存到数据库文件。conn.rollback(): 撤销当前事务中自上次commit()以来对数据库的所有更改。
4.4 close() 方法
conn.close() 用于关闭数据库连接,释放所有相关的系统资源。在完成所有数据库操作后,务必调用此方法。
5. 关闭数据库连接
正确关闭数据库连接是管理资源和确保数据一致性的关键。
5.1 手动关闭:conn.close()
在旧的或简单的脚本中,您可能会手动调用 conn.close()。
import sqlite3
conn = sqlite3.connect('manual_close.db')
try:
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS items (name TEXT)")
cursor.execute("INSERT INTO items (name) VALUES ('Item A')")
conn.commit()
print("手动关闭:数据已提交。")
except sqlite3.Error as e:
print(f"错误: {e}")
finally:
conn.close() # 确保在任何情况下都关闭连接
print("手动关闭:数据库连接已关闭。")
5.2 自动关闭:使用 with 语句 (推荐)
Python 的 with 语句(上下文管理器)是管理数据库连接的最佳实践。当 with 块结束时,无论是因为正常执行完毕还是因为发生异常,连接都会自动关闭。此外,如果 with 块正常退出,它会自动提交事务;如果块内发生异常,它会自动回滚事务。
import sqlite3
# 推荐使用 with 语句
with sqlite3.connect('auto_close.db') as conn:
print("with 语句:连接已建立。")
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS products (product_name TEXT)")
cursor.execute("INSERT INTO products (product_name) VALUES ('Product X')")
print("with 语句:数据已插入,将在块结束时自动提交。")
# 如果这里没有发生异常,conn.commit() 会在退出 with 块时自动调用。
print("with 语句块结束,数据库连接已自动关闭,事务已提交。")
# 模拟异常导致的回滚
try:
with sqlite3.connect('auto_rollback.db') as conn:
print("\nwith 语句(模拟回滚):连接已建立。")
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS logs (message TEXT)")
cursor.execute("INSERT INTO logs (message) VALUES ('Log entry 1')")
print("插入第一条日志。")
# 故意引发一个错误
raise ValueError("模拟一个意料之外的错误")
cursor.execute("INSERT INTO logs (message) VALUES ('Log entry 2')") # 这行不会被执行
except ValueError as e:
print(f"捕获到错误: {e}")
print("with 语句块结束,由于异常,事务已自动回滚。")
# 此时,'Log entry 1' 将不会被保存到数据库
except sqlite3.Error as e:
print(f"捕获到 SQLite 错误: {e}")
print("with 语句块结束,由于异常,事务已自动回滚。")
6. 连接过程中的错误处理
在连接数据库时,可能会遇到各种错误,例如数据库文件不存在(但会自动创建,所以这不是错误),文件权限问题,或者数据库被其他进程锁定。
6.1 常见错误类型
sqlite3.OperationalError: 最常见的操作错误,例如数据库文件被锁定、文件找不到(如果 URI 模式下指定了只读且文件不存在)、权限不足等。sqlite3.DatabaseError: 所有数据库相关错误的基类。sqlite3.ProgrammingError: 当使用check_same_thread=True时,在错误的线程中访问连接可能会抛出此错误。
6.2 错误处理示例
import sqlite3
import os
# 模拟一个没有写入权限的目录(或一个不存在的目录)
# Windows 用户可能需要创建一个不存在的路径来测试权限错误
# 例如:non_existent_dir/test.db
# 或者在 Linux/macOS 上尝试写入只读目录
# no_permission_db = '/root/no_permission.db' # 仅作示例,实际执行可能需要管理员权限
# 此处为了演示方便,不实际执行可能造成权限问题的代码
# 假设一个数据库文件已被其他进程锁定 (很难在简单示例中模拟)
# 或者路径包含非法字符
invalid_path_db = ':/invalid_path.db' # 路径包含非法字符,在某些系统下可能无法创建
try:
print(f"尝试连接到非法路径: '{invalid_path_db}'")
conn_bad = sqlite3.connect(invalid_path_db)
print("连接成功(这不应该发生)")
conn_bad.close()
except sqlite3.OperationalError as e:
print(f"捕获到 OperationalError: {e}")
print("错误提示表明文件路径可能存在问题或权限不足。")
except sqlite3.Error as e:
print(f"捕获到其他 SQLite 错误: {e}")
except Exception as e:
print(f"捕获到其他未知错误: {e}")
# 清理测试文件 (如果有成功创建)
if os.path.exists('my_first_db.db'):
os.remove('my_first_db.db')
if os.path.exists('manual_close.db'):
os.remove('manual_close.db')
if os.path.exists('auto_close.db'):
os.remove('auto_close.db')
if os.path.exists('auto_rollback.db'):
os.remove('auto_rollback.db')
print("\n测试文件已清理。")
7. 最佳实践与注意事项
7.1 始终使用 with 语句
这是管理 sqlite3 连接的首选方法。它确保连接在任何情况下都能被正确关闭,并根据代码块的执行结果自动提交或回滚事务。
7.2 理解 check_same_thread
- 如果您在单线程应用程序中使用 SQLite,保持
check_same_thread=True(默认) 是最安全的。 - 如果您需要在多线程环境中使用数据库,并且希望在不同线程中共享同一个连接,必须将
check_same_thread设置为False。但请务必自行处理并发访问的同步问题(例如使用threading.Lock),否则可能导致数据损坏。一个更好的多线程策略是,每个线程都有自己的数据库连接。
7.3 谨慎设置 isolation_level
- 对于大多数应用程序,默认的
'DEFERRED'隔离级别通常足够。 - 如果您需要更严格的事务控制,或者要避免其他连接在您读写之前就进行操作,可以考虑
'IMMEDIATE'或'EXCLUSIVE'。 - 避免使用
isolation_level=None,除非您非常清楚其含义以及可能导致的数据不一致问题。
7.4 正确处理文件路径
- 使用
os.path模块来构建和操作文件路径,以确保跨平台兼容性。 - 确保您的应用程序对数据库文件所在的目录有足够的读写权限。
- 对于绝对路径,请务必在字符串前加上
r(原始字符串) 或使用os.path.join来避免转义字符问题。
8. 总结
为您详细介绍了在 Python 中连接 SQLite 数据库的核心知识。
核心要点回顾:
sqlite3.connect()是建立数据库连接的关键函数。- 通过
database参数可以连接到文件数据库或内存数据库。 timeout控制等待锁的时间,isolation_level定义事务隔离级别,detect_types处理数据类型转换,check_same_thread涉及线程安全。sqlite3.connect()返回一个**Connection对象**,它是执行所有数据库操作的入口,包括创建Cursor、提交/回滚事务、关闭连接。- 关闭连接至关重要,推荐使用
with语句进行自动管理,它能确保资源释放和事务的正确处理。 - 在连接和操作过程中,通过
try-except块处理sqlite3.OperationalError等异常,以增强程序的健壮性。
掌握这些连接数据库的基础知识和最佳实践,将为您的 Python SQLite 开发打下坚实的基础。
更多推荐


所有评论(0)