1. 程式人生 > >LitePal的存儲操作

LitePal的存儲操作

list 判斷 eas 類之間的關系 date() hack date 如何使用 rod

傳統的存儲數據方式

其實最傳統的存儲數據方式肯定是通過SQL語句拼接字符串來進行存儲的,不過這種方式有點過於“傳統”了,今天我們在這裏就不討論這種情況。實際上,Android專門提供了一種用於存儲數據的簡便方法,使得我們不用編寫SQL語句就可以執行存儲操作。下面來看一下SQLiteDatabase中的insert()方法:

public long insert(String table, String nullColumnHack, ContentValues values)

可以看到,insert方法接收三個參數,第一個參數是表名,第二個參數通常都用不到,直接傳null,第三個參數則是一個封裝了待存儲數據的ContentValues對象。因此,比如說我們想往news表中插入一條新聞,就可以這樣寫:

SQLiteDatabase db = dbHelper.getWritableDatabase();  
ContentValues values = new ContentValues();  
values.put("title", "這是一條新聞標題");  
values.put("content", "這是一條新聞內容");  
values.put("publishdate", System.currentTimeMillis());  
long id = db.insert("news", null, values);  

其中,調用ContentValues的put()方法來添加待存儲數據,put()方法接收兩個參數,第一個參數是數據庫表中對應的列名,第二個參數就是要存儲的值,最後調用一下insert()方法,這條新聞就會插入到news表當中了,並且該數據行對應的id會作為返回值進行返回。

用法很簡單是嗎?確實,比起直接使用SQL語句,SQLiteDatabase中提供的insert()方法的確簡單了很多。但insert()方法也並非是那麽的完美,它還是有很多不方便的地方的,比如說沒有考慮表關聯的情況,我們需要手動對關聯表的外鍵進行存儲。再比如說,沒有提供批量存儲的功能,當我們有一個集合的數據需要存儲時,需要通過循環來遍歷這個集合,然後一次次地調用insert()方法來插入數據。

好了,那麽關於傳統存儲數據的用法就簡單介紹到這裏,因為確實沒什麽的更多的用法了,並且它也不是我們今天的主角。接下來,就讓我們看一看今天的驚喜,學習如何使用LitePal來進行數據庫存儲的操作。

使用LitePal存儲數據

LitePal中與存儲相關的API其實並不多,但用法還是頗為豐富的,而且比起傳統的insert()方法,使用LitePal來存儲數據可以簡單到讓你驚嘆的地步,那麽今天我們就來完整地學習一下LitePal存儲數據的所有用法。

在前面幾篇文章當中,我們在項目裏已經建好了News、Comment、Introduction、Category這幾個實體類,通過這些實體類,LitePal就可以把相應的表自動創建出來。現在來觀察這幾個實體類,我們發現這幾個類都是沒有繼承結構的。沒錯,因為LitePal進行表管理操作時不需要這些實體類有任何的繼承結構,當時為了簡單起見就沒有寫。但是進行CRUD操作時就不行了,LitePal要求所有的實體類都要繼承自DataSupport這個類,因此這裏我們就要把繼承結構給加上才行。修改News類的代碼,如下所示:

public class News extends DataSupport{  
      
    ......  
      
    // 自動生成get、set方法  
}  

可以看到,這裏只是讓News類繼承自了DataSupport,其它什麽都沒有改變。另外幾個Comment、Introduction、Category類也使用同樣的改法,這裏就不一一演示了。

繼承了DataSupport類之後,這些實體類就擁有了進行CRUD操作的能力,那麽比如想要存儲一條數據到news表當中,就可以這樣寫:

News news = new News();  
news.setTitle("這是一條新聞標題");  
news.setContent("這是一條新聞內容");  
news.setPublishDate(new Date());  
news.save(); 

怎麽樣?是不是非常簡單,不需要SQLiteDatabase,不需要ContentValues,不需要通過列名組裝數據,甚至不需要指定表名,只需要new出一個News對象,然後把要存儲的數據通過setter方法傳入,最後調用一下save()方法就好了,而這個save()方法自然就是從DataSupport類中繼承而來的了。

除此之外,save()方法還是有返回值的,我們可以根據返回值來判斷存儲是否成功,比如說這樣寫:

if (news.save()) {  
    Toast.makeText(context, "存儲成功", Toast.LENGTH_SHORT).show();  
} else {  
    Toast.makeText(context, "存儲失敗", Toast.LENGTH_SHORT).show();  
}  

可以看出,save()方法返回的是一個布爾值,用於表示存儲成功還是失敗,但同時也說明這個方法是不會拋出異常的。有些朋友希望如果存儲失敗的話就拋出異常,而不是返回一個false,那就可以使用saveThrows()方法來代替,如下所示:

News news = new News();  
news.setTitle("這是一條新聞標題");  
news.setContent("這是一條新聞內容");  
news.setPublishDate(new Date());  
news.saveThrows();  

使用saveThrows()方法來存儲數據,一旦存儲失敗就會拋出一個DataSupportException異常,我們可以通過對這個異常進行捕獲來處理存儲失敗的情況。

那有些細心的朋友可能已經註意到,使用的insert()方法來存儲數據時是有返回值的,返回的是插入行對應的id。但LitePal中的save()方法返回的是布爾值,那麽我們怎樣才能拿到存儲成功之後這條數據對應的id呢?對此,LitePal使用了一種非常巧妙的做法,還記得我們在每個實體類中都定義了一個id字段嗎?當調用save()方法或saveThrows()方法存儲成功之後,LitePal會自動將該條數據對應的id賦值到實體類的id字段上。讓我們來做個試驗吧,代碼如下所示:

News news = new News();  
news.setTitle("這是一條新聞標題");  
news.setContent("這是一條新聞內容");  
news.setPublishDate(new Date());  
Log.d("TAG", "news id is " + news.getId());  
news.save();  
Log.d("TAG", "news id is " + news.getId());  

在save之前打印一下news的id,在save之後再打印一次,現在運行一下,打印結果如下所示:

技術分享

OK,在save之前打印的id是0,說明此時id這個字段還沒有被賦值,在save之後打印的id是1,說明此時id已經被賦值了。那麽我們再到數據庫表中再查看一下這條記錄到底有沒有存儲成功吧,如下圖所示:

技術分享

可以看到,這條新聞確實已經存儲成功了,並且對應的id正是1,和我們前面打印的結果是一致的。

不過LitePal的存儲功能顯示不僅僅只有這些用法,事實上,LitePal在存儲數據的時候默默幫我們做了很多的事情,比如多個實體類之間有關聯關系的話,我們不需要考慮在存儲數據的時候怎麽去建立數據與數據之間的關聯,因為LitePal一切都幫我們做好了。

還是通過一個例子來看一下吧,Comment和News之間是多對一的關系,一條News中是可以包含多條評論的,因此我們就可以這樣寫:

Comment comment1 = new Comment();  
comment1.setContent("好評!");  
comment1.setPublishDate(new Date());  
comment1.save();  
Comment comment2 = new Comment();  
comment2.setContent("贊一個");  
comment2.setPublishDate(new Date());  
comment2.save();  
News news = new News();  
news.getCommentList().add(comment1);  
news.getCommentList().add(comment2);  
news.setTitle("第二條新聞標題");  
news.setContent("第二條新聞內容");  
news.setPublishDate(new Date());  
news.setCommentCount(news.getCommentList().size());  
news.save();  

可以看到,這裏先是存儲了一條comment1數據,然後存儲一條comment2數據,接著在存儲News之前先把剛才的兩個Comment對象添加到了News的commentList列表當中,這樣就表示這兩條Comment是屬於這個News對象的,最後再把News存儲到數據庫中,這樣它們之間的關聯關系就會自動建立了。讓我們查看數據庫表檢查一下吧,首先看一下news表,如下所示:

技術分享

OK,第二條新聞已經成功存儲到news表中了,這條新聞的id是2。那麽從哪裏可以看出來關聯關系呢?我們在上一篇文章中學過,多對一關聯的時候,外鍵是存放在多方的,因此關聯關系我們要到comment表中去查看,如下所示:

技術分享

可以看到,兩條評論都已經成功存儲到comment表中了,並且這兩條評論的news_id都是2,說明它們是屬於第二條新聞的。怎麽樣,僅僅是在存儲數據之前建立好實體類之間的關系,再調用一下save()方法,那麽數據之間的關聯關系就會自動建立了,是不是非常簡單?上面的代碼只是多對一情況的一種用法,還有一對一和多對多的情況,其實用法都是差不多的,相信你已經能舉一反三了。

另外,LitePal對集合數據的存儲還專門提供了一個方法,比如說我們有一個News集合,那麽應該怎樣去存儲這個集合中的每條News呢?傳統情況下可以這樣寫:

List<News> newsList;  
...  
for (News news : newsList) {  
    news.save();  
}  

通過一個循環來遍歷出這個集合中的每一個News對象,然後逐個調用save()方法。這樣的寫法當然是可以的,但是效率會比較低,因為調用save()方法的時候除了會執行存儲操作之外,還會去分析News類的關聯關系,那麽每次循環都去重新分析一遍關聯關系顯然是比較耗時的。因此,LitePal提供了一個saveAll()方法,專門用於存儲集合數據的,用法如下所示:

List<News> newsList;  
...  
DataSupport.saveAll(newsList);  

saveAll()方法接收一個Collection集合參數,只要把待存儲的集合數據傳入即可。這個方法可以完成和上面一段代碼完全一樣的功能,但效率卻會高得多,而且寫法也更加簡單。

LitePal的存儲操作