【iOS(swift)笔记-11】App版本升级时本地数据库sqlite更新逻辑

06-01 1636阅读

/*

     

     原理描述:

     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,记录数据库版本,表示等于或低于此版本的数据库都要删除)(如果新版本数据库改动太复杂,不容易复制用户数据到新数据库,不如干脆直接删除掉,重新开始记录)

     

     在版本升级的时候,为了保证用户已有数据,而且新数据库表结构字段可能增删改,不能随随便便替换数据库,而是要合理地“转移”数据。

     

   (用数据库版本为标识是否已更新数据库)

【iOS(swift)笔记-11】App版本升级时本地数据库sqlite更新逻辑
(图片来源网络,侵删)

   数据库更新处理方式:

   第1步,判断是否已更新过数据库(拿db.db和dbLocal.db中的dbVersion做比较,如果已更新过了就不用执行后续步骤)

【iOS(swift)笔记-11】App版本升级时本地数据库sqlite更新逻辑
(图片来源网络,侵删)

   第2步,原始数据库db.db->(直接复制一份)->临时数据库dbLocalTemp.db

   第3步,本地数据库dbLocal.db->(用SQL查询语句将用户数据复制到)->临时数据库dbLocalTemp.db

【iOS(swift)笔记-11】App版本升级时本地数据库sqlite更新逻辑
(图片来源网络,侵删)

     【因为新的数据库可能增加了属性或新的表,所以要以新的数据库为中心,将数据填充完整后替换。而不是在旧的正式数据库做修改,这样很麻烦而且容易遗漏】

   第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)存储,记录当前数据库的版本。用于判断数据库是否已更新)
}
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码