目錄
- 引言:數據庫連接的重要性
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()方法
- 關閉數據庫連接
- 5.1 手動關閉:
conn.close() - 5.2 自動關閉:使用
with語句 (推薦)
- 連接過程中的錯誤處理
- 6.1 常見錯誤類型
- 6.2 錯誤處理示例
- 最佳實踐與注意事項
- 7.1 始終使用
with語句 - 7.2 理解
check_same_thread - 7.3 謹慎設置
isolation_level - 7.4 正確處理文件路徑
- 總結
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 開發打下堅實的基礎。