目錄

  1. 引言:數據庫連接的重要性
  2. 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 factorycached_statements 參數
  1. 建立連接:代碼示例
  • 3.1 連接到文件數據庫 (創建或打開)
  • 3.2 連接到內存數據庫
  1. Connection 對象
  • 4.1 Connection 對象的功能
  • 4.2 cursor() 方法
  • 4.3 commit()rollback() 方法
  • 4.4 close() 方法
  1. 關閉數據庫連接
  • 5.1 手動關閉:conn.close()
  • 5.2 自動關閉:使用 with 語句 (推薦)
  1. 連接過程中的錯誤處理
  • 6.1 常見錯誤類型
  • 6.2 錯誤處理示例
  1. 最佳實踐與注意事項
  • 7.1 始終使用 with 語句
  • 7.2 理解 check_same_thread
  • 7.3 謹慎設置 isolation_level
  • 7.4 正確處理文件路徑
  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=Truedatabase 參數可以是一個 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 factorycached_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 開發打下堅實的基礎。