本文參考iOS SQLite、CoreData、FMDB數據庫詳解,非常感謝該作者
序言
在iOS開發中數據存儲的方式可以歸納為兩類:一類是存儲為文件,另一類是存儲到數據庫。例如前面IOS開發系列—Objective-C之Foundation框架的文章中提到歸檔、plist文件存儲,包括偏好設置其本質都是存儲為文件,只是說歸檔或者plist文件存儲可以選擇保存到沙盒中,而偏好設置系統已經規定只能保存到沙盒的Library/Preferences目錄。當然,文件存儲并不作為本文的重點內容。本文重點還是說數據庫存儲,做過數據庫開發的朋友應該知道,可以通過SQL直接訪問數據庫,也可以通過ORM進行對象關系映射訪問數據庫。這兩種方式恰恰對應iOS中SQLite和Core Data的內容,在此將重點進行分析:
一 SQLite
SQLite是目前主流的嵌入式關系型數據庫,其最主要的特點就是輕量級、跨平臺,當前很多嵌入式操作系統都將其作為數據庫首選。雖然SQLite是一款輕型數據庫,但是其功能也絕不亞于很多大型關系數據庫。學習數據庫就要學習其相關的定義、操作、查詢語言,也就是大家日常說得SQL語句。和其他數據庫相比,SQLite中的SQL語法并沒有太大的差別,因此這里對于SQL語句的內容不會過多贅述,大家可以參考SQLite中其他SQL相關的內容,這里還是重點講解iOS中如何使用SQLite構建應用程序。先看一下SQLite數據庫的幾個特點:
- 基于C語言開發的輕型數據庫
- 在iOS中需要使用C語言語法進行數據庫操作、訪問(無法使用ObjC直接訪問,因為libqlite3框架基于C語言編寫)
- SQLite中采用的是動態數據類型,即使創建時定義了一種類型,在實際操作時也可以存儲其他類型,但是推薦建庫時使用合適的類型(特別是應用需要考慮跨平臺的情況時)
- 建立連接后通常不需要關閉連接(盡管可以手動關閉)
在iOS中操作SQLite數據庫可以分為以下幾步(注意先在項目中導入libsqlite3框架):
1.打開數據庫,利用sqlite3_open()打開數據庫會指定一個數據庫文件保存路徑,如果文件存在則直接打開,否則創建并打開。打開數據庫會得到一個sqlite3類型的對象,后面需要借助這個對象進行其他操作。
2.執行SQL語句,執行SQL語句又包括有返回值的語句和無返回值語句。
3.對于無返回值的語句(如增加、刪除、修改等)直接通過sqlite3_exec()函數執行;
4.對于有返回值的語句則首先通過sqlite3_prepare_v2()進行sql語句評估(語法檢測),然后通過sqlite3_step()依次取出查詢結果的每一行數據,對于每行數據都可以通過對應的sqlite3_column_類型()方法獲得對應列的數據,如此反復循環直到遍歷完成。當然,最后需要釋放句柄。
在整個操作過程中無需管理數據庫連接,對于嵌入式SQLite操作是持久連接(盡管可以通過sqlite3_close()關閉),不需要開發人員自己釋放連接。縱觀整個操作過程,其實與其他平臺的開發沒有明顯的區別,較為麻煩的就是數據讀取,在iOS平臺中使用C進行數據讀取采用了游標的形式,每次只能讀取一行數據,較為麻煩。因此實際開發中不妨對這些操作進行封裝:
二 Core Data
當前,各類應用開發中只要牽扯到數據庫操作通常都會用到一個概念“對象關系映射(ORM)”。例如在Java平臺使用Hibernate,在.NET平臺使用Entity Framework、Linq、NHibernate等。在iOS中也不例外,iOS中ORM框架首選Core Data,這是官方推薦的,不需要借助第三方框架。無論是哪種平臺、哪種技術,ORM框架的作用都是相同的,那就是將關系數據庫中的表(準確的說是實體)轉換為程序中的對象,其本質還是對數據庫的操作(例如Core Data中如果存儲類型配置為SQLite則本質還是操作的SQLite數據庫)。細心的朋友應該已經注意到,在上面的SQLite中其實我們在KCMainViewController中進行的數據庫操作已經轉換為了對象操作,服務層中的方法中已經將對數據庫的操作封裝起來,轉換為了對Model的操作,這種方式已經是面向對象的。上述通過將對象映射到實體的過程完全是手動完成的,相對來說操作比較復雜,就拿對KCStatus對象的操作來說:首先要手動創建數據庫(Status表),其次手動創建模型KCStatus,接著創建服務層KCStatusService。Core Data正是為了解決這個問題而產生的,它將數據庫的創建、表的創建、對象和表的轉換等操作封裝起來,簡化了我們的操作(注意Core Data只是將對象關系的映射簡化了,并不是把服務層替代了,這一點大家需要明白)。
使用Core Data進行數據庫存取并不需要手動創建數據庫,這個過程完全由Core Data框架完成,開發人員面對的是模型,主要的工作就是把模型創建起來,具體數據庫如何創建則不用管。在iOS項目中添加“Data Model”文件。然后在其中創建實體和關系:
模型創建的過程中需要注意:
實體對象不需要創建ID主鍵,Attributes中應該是有意義屬性(創建過程中應該考慮對象的屬性而不是數據庫中表有幾個字段,盡管多數屬性會對應表的字段)。
所有的屬性應該指定具體類型(盡管在SQLite中可以不指定),因為實體對象會對應生成ObjC模型類。
實體對象中其他實體對象類型的屬性應該通過Relationships建立,并且注意實體之間的對應關系(例如一個用戶有多條微博,而一條微博則只屬于一個用戶,用戶和微博形成一對多的關系)。
有幾點需要注意:
1.所有的實體類型都繼承于NSManagedObject,每個NSManagedObject對象對應著數據庫中一條記錄。
2.集合屬性(例如User中的status)生成了訪問此屬性的分類方法。
3.使用@dynamic代表具體屬性實現,具體實現細節不需要開發人員關心。
當然,了解了這些還不足以完成數據的操作。究竟Core Data具體的設計如何,要完成數據的存取我們還需要了解一下Core Data幾個核心的類。
1.Persistent Object Store:可以理解為存儲持久對象的數據庫(例如SQLite,注意Core Data也支持其他類型的數據存儲,例如xml、二進制數據等)。
2.Managed Object Model:對象模型,對應Xcode中創建的模型文件。
3.Persistent Store Coordinator:對象模型和實體類之間的轉換協調器,用于管理不同存儲對象的上下文。
4.Managed Object Context:對象管理上下文,負責實體對象和數據庫之間的交互。
Core Data使用
Core Data使用起來相對直接使用SQLite3的API而言更加的面向對象,操作過程通常分為以下幾個步驟:
1.創建管理上下文
創建管理上下可以細分為:加載模型文件->指定數據存儲路徑->創建對應數據類型的存儲->創建管理對象上下方并指定存儲。
經過這幾個步驟之后可以得到管理對象上下文NSManagedObjectContext,以后所有的數據操作都由此對象負責。同時如果是第一次創建上下文,Core Data會自動創建存儲文件(例如這里使用SQLite3存儲),并且根據模型對象創建對應的表結構。
2.查詢數據
對于有條件的查詢,在Core Data中是通過謂詞來實現的。首先創建一個請求,然后設置請求條件,最后調用上下文執行請求的方法。
3.插入數據
插入數據需要調用實體描述對象NSEntityDescription返回一個實體對象,然后設置對象屬性,最后保存當前上下文即可。這里需要注意,增、刪、改操作完最后必須調用管理對象上下文的保存方法,否則操作不會執行。
4.刪除數據
刪除數據可以直接調用管理對象上下文的deleteObject方法,刪除完保存上下文即可。注意,刪除數據前必須先查詢到對應對象。
5.修改數據
修改數據首先也是取出對應的實體對象,然后通過修改對象的屬性,最后保存上下文。
調試
雖然Core Data(如果使用SQLite數據庫)操作最終轉換為SQL操作,但是調試起來卻不想操作SQL那么方便。特別是對于初學者而言經常出現查詢報錯的問題,如果能看到最終生成的SQL語句自然對于調試很有幫助。事實上在Xcode中是支持Core Data調試的,具體操作:Product-Scheme-Edit Scheme-Run-Arguments中依次添加兩個參數(注意參數順序不能錯):-com.apple.CoreData.SQLDebug、
注意:如果模型發生了變化,此時可以重新生成實體類文件,但是所生成的數據庫并不會自動更新,這時需要考慮重新生成數據庫并遷移原有的數據。
三 FMDB
相比于SQLite3來說Core Data存在著諸多優勢,它面向對象,開發人員不必過多的關心更多數據庫操作知識,同時它基于ObjC操作,書寫更加優雅等。但是它本身也存在著一定的限制,例如如果考慮到跨平臺,則只能選擇SQLite,因為無論是iOS還是Android都可以使用同一個數據庫,降低了開發成本和維護成本。其次是當前多數ORM框架都存在的性能問題,因為ORM最終轉化為SQL操作,其中牽扯到模型數據轉化,其性能自然比不上直接使用SQL操作數據庫。那么有沒有更好的選擇呢?答案就是對SQLite進行封裝。
其實通過前面對于SQLite的分析,大家應該已經看到KCDbManager就是對于SQLite封裝的結果,開發人員面對的只有SQL和ObjC方法,不用過多libsqlite3的C語言API。但它畢竟只是一個簡單的封裝,還有更多的細節沒有考慮,例如如何處理并發安全性,如何更好的處理事務等。因此,這里推薦使用第三方框架FMDB,整個框架非常輕量級但又不失靈活性,也是很多企業開發的首選。
步驟
1.FMDB既然是對于libsqlite3框架的封裝,自然使用起來也是類似的,使用前也要打開一個數據庫,這個數據庫文件存在則直接打開否則會創建并打開。這里FMDB引入了一個MFDatabase對象來表示數據庫,打開數據庫和后面的數據庫操作全部依賴此對象。
注意:dataWithPath中的路徑參數一般會選擇保存到沙箱中的Documents目錄中;如果這個參數設置為nil則數據庫會在內存中創建;如果設置為@””則會在沙箱中的臨時目錄創建,應用程序關閉則文件刪除。
2.對于數據庫的操作跟前面KCDbManager的封裝是類似的,在FMDB中FMDatabase類提供了兩個方法executeUpdate:和executeQuery:分別用于執行無返回結果的查詢和有返回結果的查詢。
對于有返回結果的查詢而言,查詢完返回一個游標FMResultSet,通過遍歷游標進行查詢。而且FMDB中提供了大量intForColumn、stringForColumn等方法進行取值。
并發和事務
我們知道直接使用libsqlite3進行數據庫操作其實是線程不安全的,如果遇到多個線程同時操作一個表的時候可能會發生意想不到的結果。為了解決這個問題建議在多線程中使用FMDatabaseQueue對象,相比FMDatabase而言,它是線程安全的。
創建FMDatabaseQueue的方法是類似的,調用databaseQueueWithPath:方法即可。注意這里不需要調用打開操作。
然后所有的增刪改查操作調用FMDatabaseQueue的inDatabase:方法在block中執行操作sql語句即可。
之所以將事務放到FMDB中去說并不是因為只有FMDB才支持事務,而是因為FMDB將其封裝成了幾個方法來調用,不用自己寫對應的sql而已。其實在在使用libsqlite3操作數據庫時也是原生支持事務的(因為這里的事務是基于數據庫的,FMDB還是使用的SQLite數據庫),只要在執行sql語句前加上“begin transaction;”執行完之后執行“commit transaction;”或者“rollback transaction;”進行提交或回滾即可。另外在Core Data中大家也可以發現,所有的增、刪、改操作之后必須調用上下文的保存方法,其實本身就提供了事務的支持,只要不調用保存方法,之前所有的操作是不會提交的。在FMDB中FMDatabase有beginTransaction、commit、rollback三個方法進行開啟事務、提交事務和回滾事務。
四 CoreData,sqlite3,FMDB 對比
4.1 Core Data
core data 基于model-view-controller(mvc)模式下,為創建分解的cocoa應用程序提供了一個靈活和強大的數據模型框架。
core data可以使你以圖形界面的方式快速的定義app的數據模型,同時在你的代碼中容易獲取到它。core data提供了基礎結構去處理常用的功能,例如保存,恢復,撤銷和重做,允許你在app中繼續創建新的任務。在使用core data的時候,你不用安裝額外的數據庫系統,因為core data使用內置的sqlite數據庫。
core data提供了一個通用的數據管理解決方案來處理那些所有需要數據模型的app(或大或小)。app使用core data來管理數據對象是很多的益處。
core data將你app的模型層放入到一組定義在內存中的數據對象。core data會追蹤這些對象的改變,同時可以根據需要做相反的改變,例如用戶執行撤銷命令。當core data在對你app數據的改變進行保存的時候,core data會把這些數據歸檔,并永久性保存。它保存的數據在一些常規的文件,你可以在Finder中可以進行管理,用spotlight進行搜索,備份到 cd,和email給朋友或者家人。
在使用core data框架的時候,你可以創建一個管理對象的模型,該模型提供了對模型對象的抽象定義,這也就是我們所知道的entities,它可以在我們的程序中使用。
core data是一個實體-關系模型,該模型是使用Xcode的數據模型設計工具來定義的,對數據實體以及他們的關系提供了豐富的環境。
4.2 sqlite
mac os x中sqlite庫,它是一個輕量級功能強大的關系數據引擎,也很容易嵌入到應用程序??梢栽诙鄠€平臺使用,sqlite是一個輕量級的嵌入式sql數據庫編程。與core data框架不同的是,sqlite是使用程序式的,sql的主要的API來直接操作數據表。
4.3 FMDB
FMDB框架是對sqlite很薄的封裝,主要的類也就兩個:FMDatabase和FMResultSet。在使用fmdb的時候還需要導入libsqlite3.0.dylib。
core data允許用戶使用代表實體和實體間關系的高層對象來操作數據。它也可以管理串行化的數據,提供對象生存期管理與object_graph 管理,包括存儲。Core Data直接與Sqlite交互,避免開發者使用原本的SQL語句.
4.4 選擇
在編寫程序的時候盡量使用
core data
,這樣才是最優的選擇。至于
sqlite
和fmdb
的使用情況,這個看個人喜好。fmdb就是對sqlite的封裝,使用起來有方便的接口,沒那么麻煩而已。