如何将Python类实例持久化存储到SQLite3数据库?
两种将Python类实例持久化到SQLite3的方案
方案一:结构化存储(推荐)
这种方式把类的属性映射到数据库表的字段,存储和读取时手动处理属性与数据库记录的转换,优点是数据透明、安全,便于直接操作数据库中的数据。
实现代码
import sqlite3 class Foo: def __init__(self, foo1=0, instance_id=None): self.foo1 = foo1 # 用instance_id绑定数据库中的记录ID,方便后续更新 self.instance_id = instance_id def increment(self): self.foo1 += 1 # 初始化数据库连接 def init_db(): conn = sqlite3.connect('foo_instances.db') cursor = conn.cursor() # 创建存储Foo实例的表,instance_id为主键自增 cursor.execute(''' CREATE TABLE IF NOT EXISTS foos ( instance_id INTEGER PRIMARY KEY AUTOINCREMENT, foo1 INTEGER NOT NULL ) ''') conn.commit() conn.close() # 保存Foo实例到数据库 def save_foo(foo): conn = sqlite3.connect('foo_instances.db') cursor = conn.cursor() if foo.instance_id is None: # 新实例,插入记录 cursor.execute('INSERT INTO foos (foo1) VALUES (?)', (foo.foo1,)) foo.instance_id = cursor.lastrowid else: # 已有实例,更新记录 cursor.execute('UPDATE foos SET foo1 = ? WHERE instance_id = ?', (foo.foo1, foo.instance_id)) conn.commit() conn.close() # 从数据库加载所有Foo实例 def load_all_foos(): conn = sqlite3.connect('foo_instances.db') cursor = conn.cursor() cursor.execute('SELECT instance_id, foo1 FROM foos') rows = cursor.fetchall() conn.close() # 把数据库记录转换为Foo实例 foos = [] for row in rows: instance_id, foo1 = row foo = Foo(foo1, instance_id) foos.append(foo) return foos # 示例用法 if __name__ == '__main__': init_db() # 创建实例并保存 foo = Foo() foo.increment() save_foo(foo) foo2 = Foo(5) save_foo(foo2) # 重启程序后加载实例 loaded_foos = load_all_foos() print(loaded_foos) # 输出类似 [<__main__.Foo object at 0x...>, <__main__.Foo object at 0x...>] # 修改加载后的实例并保存 for f in loaded_foos: f.increment() save_foo(f) # 再次验证 reloaded_foos = load_all_foos() for f in reloaded_foos: print(f.foo1) # 输出2和6
方案二:Pickle序列化存储
利用Python的pickle模块把整个实例序列化为二进制数据,存储到SQLite的BLOB字段中。这种方式无需手动映射属性,但数据不可读,且加载不可信数据存在安全风险,仅适合内部使用。
实现代码
import sqlite3 import pickle class Foo: def __init__(self): self.foo1 = 0 def increment(self): self.foo1 += 1 # 初始化数据库 def init_db(): conn = sqlite3.connect('foo_pickle.db') cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS foos ( id INTEGER PRIMARY KEY AUTOINCREMENT, instance_blob BLOB NOT NULL ) ''') conn.commit() conn.close() # 保存实例到数据库 def save_foo(foo): conn = sqlite3.connect('foo_pickle.db') cursor = conn.cursor() # 序列化实例为二进制 serialized = pickle.dumps(foo) cursor.execute('INSERT INTO foos (instance_blob) VALUES (?)', (serialized,)) conn.commit() conn.close() # 加载所有实例 def load_all_foos(): conn = sqlite3.connect('foo_pickle.db') cursor = conn.cursor() cursor.execute('SELECT instance_blob FROM foos') rows = cursor.fetchall() conn.close() # 反序列化二进制为实例 foos = [] for row in rows: blob = row[0] foo = pickle.loads(blob) foos.append(foo) return foos # 示例用法 if __name__ == '__main__': init_db() # 创建并保存实例 foo = Foo() foo.increment() save_foo(foo) foo2 = Foo() save_foo(foo2) # 加载实例 loaded_foos = load_all_foos() print(loaded_foos) # 输出类似 [<__main__.Foo object at 0x...>, <__main__.Foo object at 0x...>] # 修改并重新保存(注意:若要更新原实例,需记录id并执行UPDATE操作,示例为演示重新插入新记录) for f in loaded_foos: f.increment() save_foo(f) reloaded = load_all_foos() for f in reloaded: print(f.foo1) # 输出2,1,2,1
注意事项
- 方案二中如果需要更新原实例,需在表中记录id,保存时根据id更新对应的blob字段,避免重复插入。
- Pickle序列化存在版本兼容性问题,如果后续修改了Foo类的结构,旧的序列化数据可能无法正常加载。
内容的提问来源于stack exchange,提问作者Angel
相关产品推荐
相关产品推荐

