如何通过FireDAC实现SQLite数据库的独占连接?
首先得明确核心原因:FireDAC在默认情况下会复用同一进程内参数相同的SQLite连接——不管你创建多少个FDConnection实例,只要连接参数(数据库路径、用户名密码等)一致,它们都会共享同一个底层SQLite连接句柄。这就是为什么你用第二个FDConnection打开同一个数据库时不会报错,而UniDAC/ZeosLib这类组件会触发独占锁冲突的根本原因。
为什么手动设置PRAGMA locking_mode = EXCLUSIVE无效?
这个PRAGMA是连接级别的配置,它只会作用于当前的底层SQLite连接。但由于FireDAC的连接共享机制,你创建的第二个FDConnection并没有新建连接,而是直接复用了第一个连接的句柄——既然是同一个连接,自然不存在“第二个连接抢占锁”的情况,所以不会触发报错。哪怕你在第一个连接里执行了写操作,第二个FDConnection本质上还是在同一个连接里操作,当然不会有冲突。
如何实现真正的独占锁?
要解决这个问题,关键是打破FireDAC的连接共享机制,让每个FDConnection实例都创建独立的底层连接,具体有几种方式:
1. 针对单个连接禁用共享缓存
在打开连接前,给FDConnection的参数添加SharedCache=False,同时设置LockingMode为lmExclusive:
FDConnection1.Params.Database := DB_PATH; // 禁用当前连接的共享缓存,确保创建独立连接 FDConnection1.Params.Add('SharedCache=False'); // 设置FireDAC的独占锁模式 FDConnection1.LockingMode := lmExclusive; FDConnection1.Open(); // 执行写操作触发独占锁 FDQuery1.SQL.Text := 'update admin set last_write = 2'; FDQuery1.ExecSQL; // 此时打开第二个连接会触发独占锁报错 FDConnection2.Params.Database := DB_PATH; FDConnection2.Params.Add('SharedCache=False'); FDConnection2.Open();
2. 全局禁用FireDAC连接共享
如果你希望所有FDConnection都使用独立连接,可以修改FDManager的全局设置:
// 在应用启动时设置,影响所有FireDAC连接 FDManager1.ConnectionDefs.SharedConnections := False; // 后续创建的FDConnection都会使用独立连接 FDConnection1.Params.Database := DB_PATH; FDConnection1.LockingMode := lmExclusive; FDConnection1.Open();
3. 更可靠的PRAGMA设置方式
如果习惯用PRAGMA配置,建议直接把PRAGMA加入到连接参数中,这样连接打开时会自动执行,避免手动执行的时机问题:
FDConnection1.Params.Database := DB_PATH; FDConnection1.Params.Add('SharedCache=False'); // 连接打开时自动执行独占锁PRAGMA FDConnection1.Params.Add('Pragma=locking_mode=EXCLUSIVE'); FDConnection1.Open();
这样设置后,当你尝试用第二个独立的FDConnection打开同一个数据库时,就会触发SQLite的独占锁冲突报错,符合你的预期效果。
内容的提问来源于stack exchange,提问作者RaelB

