Swift-Realm數據庫的使用詳解

Swift-Realm數據庫的使用詳解

mobileDB_realm

概述

Realm 是一個跨平臺的移動數據庫引擎,其性能要優于 Core Data 和 FMDB - 移動端數據庫性能比較, 我們可以在 Android 端 realm-java,iOS端:Realm-Cocoa,同時支持 OC 和 Swift兩種語言開發。其使用簡單,免費,性能優異,跨平臺的特點廣受程序員GG喜愛。

Realm 中文文檔

本文將結合一些實戰演練講解 Realm 的用法,干貨滿滿!

Realm 支持如下屬性的存儲

  • Int,Int8,Int16,Int32 和 Int64
  • Boolean 、 Bool
  • Double 、 Float
  • String
  • NSDate 、 Date(精度到秒)
  • NSData 、 Data
  • 繼承自 Object 的類 => 作為一對一關系(Used for One-to-one relations)
  • List => 作為一對多關系(Used for one-to-many relations)

如下表是在代碼中聲明的實例:

類型 非可選值形式 可選值形式
Bool dynamic var value = false let value = RealmOptional<Bool>()
Int dynamic var value = 0 let value = RealmOptional<Int>()
Float dynamic var value: Float = 0.0 let value = RealmOptional<Float>()
Double dynamic var value: Double = 0.0 let value = RealmOptional<Double>()
String dynamic var value = "" dynamic var value: String? = nil
Data dynamic var value = NSData() dynamic var value: NSData? = nil
Date dynamic var value = NSDate() dynamic var value: NSDate? = nil
Object 必須是可選值 dynamic var value: Class?
List let value = List<Class>() 必須是非可選值
LinkingObjects let value = LinkingObjects(fromType: Class.self, property: "property") 必須是非可選值

Realm 安裝 - 使用 CocoaPods

pod 'RealmSwift'
pod 'Realm'

Realm 配置

  • 將以下代碼寫在 AppDelegate 的 didFinishLaunchingWithOptions 方法中,這個方法主要用于數據模型屬性增加或刪除時的數據遷移,每次模型屬性變化時,將 dbVersion 加 1 即可,Realm 會自行檢測新增和需要移除的屬性,然后自動更新硬盤上的數據庫架構,移除屬性的數據將會被刪除。
/// 配置數據庫
    public class func configRealm() {
        /// 如果要存儲的數據模型屬性發生變化,需要配置當前版本號比之前大
        let dbVersion : UInt64 = 2
        let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
        let dbPath = docPath.appending("/defaultDB.realm")
        let config = Realm.Configuration(fileURL: URL.init(string: dbPath), inMemoryIdentifier: nil, syncConfiguration: nil, encryptionKey: nil, readOnly: false, schemaVersion: dbVersion, migrationBlock: { (migration, oldSchemaVersion) in
            
        }, deleteRealmIfMigrationNeeded: false, shouldCompactOnLaunch: nil, objectTypes: nil)
        Realm.Configuration.defaultConfiguration = config
        Realm.asyncOpen { (realm, error) in
            if let _ = realm {
                print("Realm 服務器配置成功!")
            }else if let error = error {
                print("Realm 數據庫配置失敗:\(error.localizedDescription)")
            }
        }
    }

定義模型

import UIKit
import RealmSwift

class Book: Object {
    @objc dynamic var name = ""
    @objc dynamic var author = ""
    
    /// LinkingObjects 反向表示該對象的擁有者
    let owners = LinkingObjects(fromType: Student.self, property: "books")
}

class Student: Object {
    @objc dynamic var name = ""
    @objc dynamic var age = 18
    @objc dynamic var weight = 156
    @objc dynamic var id = 0
    @objc dynamic var address = ""
    @objc dynamic var birthday : NSDate? = nil
    @objc dynamic var photo : NSData?  = nil
    
    //重寫 Object.primaryKey() 可以設置模型的主鍵。
    //聲明主鍵之后,對象將被允許查詢,更新速度更加高效,并且要求每個對象保持唯一性。
    //一旦帶有主鍵的對象被添加到 Realm 之后,該對象的主鍵將不可修改。
    override static func primaryKey() -> String? {
        return "id"
    }
    
    //重寫 Object.ignoredProperties() 可以防止 Realm 存儲數據模型的某個屬性
    override static func ignoredProperties() -> [String] {
        return ["tempID"]
    }
    
    //重寫 Object.indexedProperties() 方法可以為數據模型中需要添加索引的屬性建立索引,Realm 支持為字符串、整型、布爾值以及 Date 屬性建立索引。
    override static func indexedProperties() -> [String] {
        return ["name"]
    }
    
    //List 用來表示一對多的關系:一個 Student 中擁有多個 Book。
    let books = List<Book>()
}

需要注意的是:在使用Realm中存儲的數據模型都要是 Object 類的子類。

1) 設置主鍵 - primaryKey

重寫 Object.primaryKey() 可以設置模型的主鍵。
聲明主鍵之后,對象將被允許查詢,更新速度更加高效,并且要求每個對象保持唯一性。
一旦帶有主鍵的對象被添加到 Realm 之后,該對象的主鍵將不可修改。

    override static func primaryKey() -> String? {
        return "id"
    }

2) 忽略屬性 - ignoredProperties

重寫 Object.ignoredProperties() 可以防止 Realm 存儲數據模型的某個屬性。Realm 將不會干涉這些屬性的常規操作,它們將由成員變量(var)提供支持,并且您能夠輕易重寫它們的 setter 和 getter。

    override static func ignoredProperties() -> [String] {
        return ["tempID"]
    }

3)索引屬性 - indexedProperties

重寫 Object.indexedProperties() 方法可以為數據模型中需要添加索引的屬性建立索引,Realm 支持為字符串、整型、布爾值以及 Date 屬性建立索引。

    override static func indexedProperties() -> [String] {
        return ["name"]
    }

4)使用List實現一對多關系 - indexedProperties

List 用來表示一對多的關系:一個 Student 中擁有多個 Book。
List 中可以包含簡單類型的 Object,表面上和可變的 Array 非常類似,所用的方法和訪問數據的方式(索引和下標)都相同,并且所包含的所有對象都應該是相同類型的。聲明前面不可加 dynamic ,因為在 Objective-C 運行時無法表示泛型屬性。
注意:List 只能夠包含 Object 類型,不能包含諸如String之類的基礎類型。

    //List 用來表示一對多的關系:一個 Student 中擁有多個 Book。
    let books = List<Book>()

5)反向關系 - LinkingObjects

通過反向關系(也被稱為反向鏈接(backlink)),您可以通過一個特定的屬性獲取和給定對象有關系的所有對象。 Realm 提供了“鏈接對象 (linking objects)” 屬性來表示這些反向關系。借助鏈接對象屬性,您可以通過指定的屬性來獲取所有鏈接到指定對象的對象。
例如:一個 Book 對象可以擁有一個名為 owners 的鏈接對象屬性,這個屬性中包含了某些 Student 對象,而這些 Student 對象在其 books 屬性中包含了這一個確定的 Book 對象。您可以將 owners 屬性設置為 LinkingObjects 類型,然后指定其關系,說明其當中包含了其擁有者 Student 對象。

  let owners = LinkingObjects(fromType: Student.self, property: "books")

1 增

1.1 需求: 插入 1 名學生信息到本地數據庫?

import UIKit
import RealmSwift
class XWStudentRealmTool: Object {
    private class func getDB() -> Realm {
        let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
        let dbPath = docPath.appending("/defaultDB.realm")
        /// 傳入路徑會自動創建數據庫
        let defaultRealm = try! Realm(fileURL: URL.init(string: dbPath)!)
        return defaultRealm
    }
}
/// 增
extension XWStudentRealmTool {
   
    public class func insertStudent(by student : Student) -> Void {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            defaultRealm.add(student)
        }
        print(defaultRealm.configuration.fileURL ?? "")
    }
}
測試代碼
  func testInsterStudent() {
        let stu = Student()
        stu.name = "極客學偉"
        stu.age = 26
        stu.id = 2;
        
        let birthdayStr = "1993-06-10"
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "YYYY-MM-dd"
        stu.birthday = dateFormatter.date(from: birthdayStr)! as NSDate
        
        stu.weight = 160
        stu.address = "回龍觀"
        
        XWStudentRealmTool.insertStudent(by: stu)
    }

通過 Realm Browser 查看剛才保存的數據
通過 print(realm.configuration.fileURL ?? "") 打印數據路徑

效果:


Snip20180529_13

1.2 需求: 測試在數據庫中插入一個擁有多本書并且有頭像的學生對象

測試代碼
//測試在數據庫中插入一個擁有多本書并且有頭像的學生對象
    func testInsertStudentWithPhotoBook() {
        let stu = Student()
        stu.name = "極客學偉_有頭像_有書"
        stu.weight = 151;
        stu.age = 26
        stu.id = 3;
        // 頭像
        stu.setPhotoWitName("cat")

        let bookFubaba = Book.init(name: "富爸爸窮爸爸", author: "[美]羅伯特.T.清崎")
        let bookShengmingbuxi = Book.init(name: "生命不息, 折騰不止", author: "羅永浩")
        let bookDianfuzhe = Book(value: ["顛覆著: 周鴻祎自傳","周鴻祎"])
        stu.books.append(bookFubaba);
        stu.books.append(bookShengmingbuxi);
        stu.books.append(bookDianfuzhe);
        
        XWStudentRealmTool.insertStudent(by: stu)
    }

運行結果:
會自動創建數據庫并新建兩個表 Student 和 Book, 并將兩者進行關聯


Snip20180530_17

Snip20180530_18

1.3 需求: 測試在數據庫中插入44個的學生對象

保存方法

/// 保存一些Student
    public class func insertStudents(by students : [Student]) -> Void {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            defaultRealm.add(students)
        }
        print(defaultRealm.configuration.fileURL ?? "")
    }
測試代碼
    //測試在數據庫中插入多個擁有多本書并且有頭像的學生對象
    func testInsertManyStudent() {
        var stus = [Student]()
        
        for i in 100...144 {
            let stu = Student()
            stu.name = "極客學偉_\(i)"
            stu.weight = 151;
            stu.age = 26
            stu.id = i;
            // 頭像
            stu.setPhotoWitName("cat")
            let birthdayStr = "1993-06-10"
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "YYYY-MM-dd"
            stu.birthday = dateFormatter.date(from: birthdayStr)! as NSDate
            stus.append(stu)
        }
        
        XWStudentRealmTool.insertStudents(by: stus)
    }

演示結果:


Snip20180530_22

2 查

2.1 普通查詢: 查詢數據庫中所有學生模型并輸出姓名,圖片,所擁有的書信息

  /// 獲取 所保存的 Student
    public class func getStudents() -> Results<Student> {
        let defaultRealm = self.getDB()
        return defaultRealm.objects(Student.self)
    }
測試代碼
     let stus = XWStudentRealmTool.getStudents()
        for stu in stus {
            print(stu.name)
            if stu.photo != nil {
                self.imageV.image = stu.getPhotoImage()
            }
            if stu.books.count > 0 {
                for book in stu.books {
                    print(book.name + "+" + book.author)
                }
            }
        }

演示結果:
輸出姓名和書籍信息


Snip20180530_20

將獲取的頭像為imageView賦值


Snip20180530_21

2.2 主鍵查詢: 查詢數據庫中id 為 110 的學生模型并輸出姓名

    /// 獲取 指定id (主鍵) 的 Student
    public class func getStudent(from id : Int) -> Student? {
        let defaultRealm = self.getDB()
        return defaultRealm.object(ofType: Student.self, forPrimaryKey: id)
    }
測試代碼
 // 通過主鍵查詢
    func testSearchStudentByID(){
        let student = XWStudentRealmTool.getStudent(from: 110)
        if let studentL = student {
            print(studentL.name)
        }
    }

演示結果:


Snip20180530_24

對應數據庫中:


Snip20180530_26

2.2 主鍵查詢: 查詢數據庫中id 為 110 的學生模型并輸出姓名

    /// 獲取 指定條件 的 Student
    public class func getStudentByTerm(_ term: String) -> Results<Student> {
        let defaultRealm = self.getDB()
        print(defaultRealm.configuration.fileURL ?? "")
        let predicate = NSPredicate(format: term)
        let results = defaultRealm.objects(Student.self)
        return  results.filter(predicate)
    }
測試代碼
    // 條件查詢
    func testSearchTermStudent()  {
        let students = XWStudentRealmTool.getStudentByTerm("name = '極客學偉_110'")
        if students.count == 0 {
            print("未查詢到任何數據")
            return
        }
        for student in students {
            print(student.name,student.weight)
        }
    }

輸出結果:


Snip20180530_27

升序/降序 查詢

// 根據名字升序查詢
let stus = realm.objects(Student.self).sorted(byKeyPath: "id")

// 根據名字降序序查詢
let stus = realm.objects(Student.self).sorted(byKeyPath: "id", ascending: false)

3 改

3.1 主鍵更新 - 更新單個學生

    /// 更新單個 Student
    public class func updateStudent(student : Student) {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            defaultRealm.add(student, update: true)
        }
    }

3.2 主鍵更新 - 更新多個學生

    /// 更新多個 Student
    public class func updateStudent(students : [Student]) {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            defaultRealm.add(students, update: true)
        }
    }

測試代碼

    /// 批量更改
    func testUpdateStudents() {
        var stus = [Student]()
        for i in 100...144 {
            let stu = Student()
            stu.name = "極客學偉改名_\(i)"
            stu.weight = 148;
            stu.age = 27
            stu.id = i;
            stus.append(stu)
        }
        XWStudentRealmTool.updateStudent(students: stus)
    }

更新之前:


Snip20180530_30

更新之后:


Snip20180530_31

3.3 鍵值更新 - 所有學生 年齡 改為 18

    /// 更新多個 Student
    public class func updateStudentAge(age : Int) {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            let students = defaultRealm.objects(Student.self)
            students.setValue(age, forKey: "age")
        }
    }

測試代碼:

    /// 批量更改年齡
    func testUpdateStudentsAge() {
        XWStudentRealmTool.updateStudentAge(age: 18)
    }

演示結果:


Snip20180530_32

4 刪

     /// 刪除單個 Student
    public class func deleteStudent(student : Student) {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            defaultRealm.delete(student)
        }
    }
    
    /// 刪除多個 Student
    public class func deleteStudent(students : Results<Student>) {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            defaultRealm.delete(students)
        }
    }

測試代碼:

    /// 刪除id 為 3 的學生
    func testDeleteOneStudent() {
        let stu = XWStudentRealmTool.getStudent(from: 3)
        if stu != nil {
            XWStudentRealmTool.deleteStudent(student: stu!)
        }
    }
    
    /// 刪除所有
    func testDeleteAllStudent() {
        let stus = XWStudentRealmTool.getStudents()
        XWStudentRealmTool.deleteStudent(students: stus)
    }

刪除后的數據庫:


Snip20180530_33

空空如也

文中所有演示代碼 -> XWRealmSwiftDemo In Github

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容