【iOS(swift)笔记-11】App版本升级时本地数据库sqlite更新逻辑
/*
原理描述:
Resources资源同名文件在包更新时会被替换掉,所以为了保留用户本地已有数据,数据库需要采用不同文件名进行操作,具体如下
// 常量配置(sqlite数据库统一用.db扩展名)
static let dbName = "db" // 原始数据库,应用的原始空数据库(db.db,这里没有扩展名是因为调用的方法程序解析方便)
static let dbLocalName = "dbLocal.db" // 用户本地数据库,就是db.db的复制品,只是记录了用户的数据
static let dbLocalTempName = "dbLocalTemp.db" // 更新数据库时用的临时数据库
// 数据库版本(数据库表中设置一个字段dbVersion,可用数字(比如1或字符串1.0.0)存储,记录当前数据库的版本。用于判断数据库是否已更新)
// 需要直接删除的旧版本数据库(数据库字段deleteDB,记录数据库版本,表示等于或低于此版本的数据库都要删除)(如果新版本数据库改动太复杂,不容易复制用户数据到新数据库,不如干脆直接删除掉,重新开始记录)
在版本升级的时候,为了保证用户已有数据,而且新数据库表结构字段可能增删改,不能随随便便替换数据库,而是要合理地“转移”数据。
(用数据库版本为标识是否已更新数据库)
数据库更新处理方式:
第1步,判断是否已更新过数据库(拿db.db和dbLocal.db中的dbVersion做比较,如果已更新过了就不用执行后续步骤)
第2步,原始数据库db.db->(直接复制一份)->临时数据库dbLocalTemp.db
第3步,本地数据库dbLocal.db->(用SQL查询语句将用户数据复制到)->临时数据库dbLocalTemp.db
【因为新的数据库可能增加了属性或新的表,所以要以新的数据库为中心,将数据填充完整后替换。而不是在旧的正式数据库做修改,这样很麻烦而且容易遗漏】
第4步,删除本地数据库dbLocal.db
第5步,临时数据库dbLocalTemp.db改名为dbLocal.db
数据库表的设计越复杂,具体复制数据的脚本就会越麻烦,所以最好是用服务器数据库存储数据,这样就算数据库改动也方便操作。本地数据库没事就别瞎改动了。
下面代码可以增加一层判断:判断是否需要直接删除旧版本数据库,如果不删则再判断是否需要更新
if(需要删除){
// 1、删除旧版本数据库
// 2、将数据库复制过去
}
else{
// 判断是否需要更新
}
自己动手,孤懒得改,反正没几个人看。。。
*/
// // DBControl.swift // BRMac // // Created by K on 2025/5/26. // /* 注,操作SQLite数据库采用第三方 SQLite.swift https://gitter.im/stephencelis/SQLite.swift */ import AppKit import SQLite class DBControl { static func initializeDB() { // 数据库源文件,放到了项目的资源根目录下(与Info.plist同级) let dbPath = Bundle.main.path(forResource: Config.dbName, ofType: "db")! let dbURL = URL(fileURLWithPath: dbPath) // 在沙盒中路径 let dbLocalURL = sandURL(filename: Config.dbLocalName) let dbLocalTempURL = sandURL(filename: Config.dbLocalTempName) print("dbURL=\(dbURL)") print("dbLocalURL=\(dbLocalURL)") print("dbLocalTempURL=\(dbLocalTempURL)") let fileManager = FileManager.default if !fileManager.fileExists(atPath: dbLocalURL.path) { print("没有本地数据库") // 没有数据库,直接复制数据库过去 do { try fileManager.copyItem(atPath: dbPath, toPath: dbLocalURL.path) print("复制成功") } catch { print("复制失败") } } else { // 有数据库 print("有数据库"); // 判断是否已更新 var dbVersion:String = "" var dbLocalVersion:String = "" do { let dbCon = try Connection(dbPath) let dbLocalCon = try Connection(dbLocalURL.path) dbVersion = try dbCon.scalar("SELECT value FROM Records where 1=1 and name='DBVersion'") as! String // 根据自己创建的表结构进行查询 dbLocalVersion = try dbLocalCon.scalar("SELECT value FROM Records where 1=1 and name='DBVersion'") as! String } catch { } print("dbLocalVersion=\(dbLocalVersion)") print("dbVersion=\(dbVersion)") if (Int(dbLocalVersion) ?? 0 >= Int(dbVersion) ?? 0) { // 已更新过了,无需再更新 print("已更新过了,无需再更新"); } else { // 需要更新 print("需要更新"); // 复制临时数据库 do { if fileManager.fileExists(atPath: dbLocalTempURL.path){ try fileManager.removeItem(at: URL(fileURLWithPath: dbLocalTempURL.path)) // 如果有先删掉 } try fileManager.copyItem(atPath: dbPath, toPath: dbLocalTempURL.path) print("临时数据库复制成功") } catch { } // 更新数据(!!!根据表的变动情况,灵活采用更新的方式) do { // 1、复制数据 // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓这里有一大堆代码要写↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // 将dbLocal.db内表的数据(仅用户记录的那部分数据即可,定死的配置类的数据就不用管了)复制到dbLocalTemp.db // 就是写一大堆SQL语句执行 print("数据复制成功") // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑这里有一大堆代码要写↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // 2、删除本地数据库dbLocal.db try fileManager.removeItem(at: URL(fileURLWithPath: dbLocalURL.path)) print("删除本地数据库dbLocal.db成功") // 3、临时数据库dbLocalTemp.db改名为dbLocal.db try fileManager.moveItem(at: URL(fileURLWithPath: dbLocalTempURL.path), to: URL(fileURLWithPath: dbLocalURL.path)) print("临时数据库dbLocalTemp.db改名为dbLocal.db成功") } catch { } } } } static func sandURL(filename: String) -> URL { /* 试图访问或修改沙盒外的文件,需要明确地请求了相关权限。 否则, 所有对用户数据的写操作(比如改名,修改内容等)都应该限于应用的沙盒目录(如 Documents、Library、Caches 等), 也就是说要先把要操作的文件都复制到沙盒里,然后再对沙盒里的文件进行操作 */ // 找出沙盒的路径 let sandPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! return URL(fileURLWithPath: sandPath).appendingPathComponent(filename) } } class Config { // 常量配置(sqlite数据库统一用.db扩展名) static let dbName = "db" // 原始数据库,应用的原始空数据库(db.db,这里没有扩展名是因为调用的方法程序解析方便) static let dbLocalName = "dbLocal.db" // 用户本地数据库,就是db.db的复制品,只是记录了用户的数据 static let dbLocalTempName = "dbLocalTemp.db" // 更新数据库时用的临时数据库 // 数据库版本(数据库表中有一个字段dbVersion,可用数字(比如1或字符串1.0.0)存储,记录当前数据库的版本。用于判断数据库是否已更新) }