Android數據庫高手祕籍(七)——體驗LitePal的查詢藝術

使用LitePal查詢數據

LitePal在查詢方面提供了非常豐富的API,功能多種多樣,基本上已經能夠滿足我們平時所有的查詢需求了。不僅如此,LitePal在查詢API的設計方面也是非常用心,摒棄了原生query()方法中繁瑣的參數列表,而是改用了一種更爲靈巧的方式——連綴查詢。除此之外,LitePal查詢的結果也不再返回Cursor對象,然後再由開發者自己去逐個取出,而是直接返回封裝好的對象。這些改變都使得查詢數據變得更加簡單,也更加合理,那麼下面我們就來完整地學習一下LitePal中查詢數據的所有用法。

簡單查詢

比如說現在我們想實現一個最簡單的功能,查詢news表中id爲1的這條記錄,使用LitePal就可以這樣寫:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. News news = DataSupport.find(News.class1);  

天吶!有沒有覺得太輕鬆了?僅僅一行代碼,就可以把news表中id爲1的記錄查出來了,而且結果還是自動封裝到News對象裏的,也不需要我們手動再從Cursor中去解析。如果是用原生的SQL語句,或者query()方法來寫,至少要20行左右的代碼才能完成同樣的功能!

那我們先冷靜一下,來分析分析這個find()方法。可以看到,它的參數列表也比較簡單,只接收兩個參數,第一個參數是一個泛型類,也就是說我們在這裏指定什麼類,返回的對象就是什麼類,所以這裏傳入News.class,那麼返回的對象也就是News了。第二個參數就更簡單了,就是一個id值,如果想要查詢id爲1的記錄就傳1,想查id爲2的記錄就傳2,以此類推。

本來一個還算頗爲複雜的功能,通過LitePal之後就變得這麼簡單了!那麼你可能已經迫不及待地想要學習更多LitePal中更多的查詢用法了,彆着急,我們一個個來看。

你也許遇到過以下場景,在某些情況下,你需要取出表中的第一條數據,那麼傳統的做法是怎麼樣的呢?在SQL語句中指定一個limit值,然後獲取返回結果的第一條記錄。但是在LitePal中不用這麼麻煩,比如我們想要獲取news表中的第一條數據,只需要這樣寫:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. News firstNews = DataSupport.findFirst(News.class);  
OK,語義性非常強吧,讓人一眼就看懂是什麼意思了,只需調用findFirst()方法,然後傳入News類,得到的就是news表中的第一條數據了。

那我們舉一翻三,如果是想要獲取News表中的最後一條數據該怎麼寫呢?同樣簡單,如下所示:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. News lastNews = DataSupport.findLast(News.class);  
因爲獲取表中第一條或者是最後一條數據的場景比較常見,所以LitePal特意提供了這兩個方法來方便我們的操作。

那麼我們看到這裏,目前都只是查詢單條數據的功能,如果想要查詢多條數據該怎麼辦呢?比如說,我們想把news表中id爲1、3、5、7的數據都查出來,該怎麼寫呢?也許有的朋友會比較聰明,立馬就想到可以一個個去查,就調用四次find()方法嘛,然後把1、3、5、7這四個id分別傳進去不就可以了。沒錯,這樣做完全是可以的,而且效率也並不低,但是LitePal給我們提供了一個更簡便的方法——findAll()。這個方法的用法和find()方法是非常類似的,只不過它可以指定多個id,並且返回值也不再是一個泛型類對象,而是一個泛型類集合,如下所示:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. List<News> newsList = DataSupport.findAll(News.class1357);  
可以看到,首先我們是調用的findAll()方法,然後這個方法的第一個參數仍然是指定的泛型類,但是後面的參數就很隨意了,你可以傳入任意個id進去,findAll()方法會把所有傳入的id所對應的數據全部查出來,然後一起返回到List<News>這個泛型集合當中。

雖說這個語法設計算是相當人性化,但是在有些場景或許不太適用,因爲可能要你要查詢的多個id已經封裝到一個數組裏了。那麼沒關係,findAll()方法也是接收數組參數的,所以說同樣的功能你也可以這樣寫:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. long[] ids = new long[] { 1357 };  
  2. List<News> newsList = DataSupport.findAll(News.class, ids);  
看到這裏,那有的朋友可能會奇怪了,說findAll()方法不應該是查詢所有數據的意思嗎?怎麼總是查詢幾個id所對應數據呢?哈!這個問題問得好,因爲findAll()方法也是可以查詢所有數據的,而且查詢所有數據的寫法更簡單,只需要這樣寫:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. List<News> allNews = DataSupport.findAll(News.class);  
看到沒有,我們只需要把後面的參數都去掉,在不指定具體id的情況下,findAll()方法查詢出的就是news表中的所有數據了,是不是語義性非常強?

而且大家不要以爲剛纔這些都只是findAll()的幾個方法重載而已,實際上剛纔我們的這幾種用法都是調用的同一個findAll()方法!一個方法卻能夠實現多種不同的查詢效果,並且語義性也很強,讓人一看就能理解,這就是LitePal的查詢藝術!

連綴查詢

當然了,LitePal給我們提供的查詢功能還遠遠不只這些,好戲還在後頭。相信大家現在也已經發現了,我們目前的查詢功能都是基於id來進行查詢的,並不能隨意地指定查詢條件。那麼怎樣才能指定查詢條件呢?讓我們回想一下傳統情況應該怎麼做,query()方法中接收七個參數,其中第三和第四個參數就是用於指定查詢條件的,然後其它幾個參數都填null就可以了。但是呢,前面我們已經痛批過了這種寫法,因爲冗長的參數列表太過繁瑣,那麼LitePal又是怎麼解決這個問題的呢?我們現在就來學習一下。

爲了避免冗長的參數列表,LitePal採用了一種非常巧妙的解決方案,叫作連綴查詢,這種查詢很靈活,可以根據我們實際的查詢需求來動態配置查詢參數。 那這裏舉個簡單的例子,比如我們想查詢news表中所有評論數大於零的新聞,就可以這樣寫:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. List<News> newsList = DataSupport.where("commentcount > ?""0").find(News.class);  
可以看到,首先是調用了DataSupport的where()方法,在這裏指定了查詢條件。where()方法接收任意個字符串參數,其中第一個參數用於進行條件約束,從第二個參數開始,都是用於替換第一個參數中的佔位符的。那這個where()方法就對應了一條SQL語句中的where部分。

接着我們在where()方法之後直接連綴了一個find()方法,然後在這裏指定一個泛型類,表示用於查詢哪張表。那麼上面的一段代碼,查詢出的結果和如下SQL語句是相同的:

[sql] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. select * from users where commentcount > 0;  

但是這樣會將news表中所有的列都查詢出來,也許你並不需要那麼多的數據,而是隻要title和content這兩列數據。那麼也很簡單,我們只要再增加一個連綴就行了,如下所示:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. List<News> newsList = DataSupport.select("title""content")  
  2.         .where("commentcount > ?""0").find(News.class);  

可以看到,這裏我們新增了一個select()方法,這個方法接收任意個字符串參數,每個參數要求對應一個列名,這樣就只會把相應列的數據查詢出來了,因此select()方法對應了一條SQL語句中的select部分。

那麼上面的一段代碼,查詢出的結果和如下SQL語句是相同的:

[sql] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. select title,content from users where commentcount > 0;  
很好玩吧?不過這還不算完呢,我們還可以繼續連綴更多的東西。比如說,我希望將查詢出的新聞按照發布的時間倒序排列,即最新發布的新聞放在最前面,那就可以這樣寫:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. List<News> newsList = DataSupport.select("title""content")  
  2.         .where("commentcount > ?""0")  
  3.         .order("publishdate desc").find(News.class);  
order()方法中接收一個字符串參數,用於指定查詢出的結果按照哪一列進行排序,asc表示正序排序,desc表示倒序排序,因此order()方法對應了一條SQL語句中的order by部分。

那麼上面的一段代碼,查詢出的結果和如下SQL語句是相同的:

[sql] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. select title,content from users where commentcount > 0 order by publishdate desc;  
然後呢,也許你並不希望將所有條件匹配的結果一次性全部查詢出來,因爲這樣數據量可能會有點太大了,而是希望只查詢出前10條數據,那麼使用連綴同樣可以輕鬆解決這個問題,代碼如下所示:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. List<News> newsList = DataSupport.select("title""content")  
  2.         .where("commentcount > ?""0")  
  3.         .order("publishdate desc").limit(10).find(News.class);  
這裏我們又連綴了一個limit()方法,這個方法接收一個整型參數,用於指定查詢前幾條數據,這裏指定成10,意思就是查詢所有匹配結果中的前10條數據。

那麼上面的一段代碼,查詢出的結果和如下SQL語句是相同的:

[sql] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. select title,content from users where commentcount > 0 order by publishdate desc limit 10;  
剛纔我們查詢到的是所有匹配條件的前10條新聞,那麼現在我想對新聞進行分頁展示,翻到第二頁時,展示第11到第20條新聞,這又該怎麼實現呢?沒關係,在LitePal的幫助下,這些功能都是十分簡單的,只需要再連綴一個偏移量就可以了,如下所示:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. List<News> newsList = DataSupport.select("title""content")  
  2.         .where("commentcount > ?""0")  
  3.         .order("publishdate desc").limit(10).offset(10)  
  4.         .find(News.class);  
可以看到,這裏我們又添加了一個offset()方法,用於指定查詢結果的偏移量,這裏指定成10,就表示偏移十個位置,那麼原來是查詢前10條新聞的,偏移了十個位置之後,就變成了查詢第11到第20條新聞了,如果偏移量是20,那就表示查詢第21到第30條新聞,以此類推。因此,limit()方法和offset()方法共同對應了一條SQL語句中的limit部分。

那麼上面的一段代碼,查詢出的結果和如下SQL語句是相同的:

[sql] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. select title,content from users where commentcount > 0 order by publishdate desc limit 10,10;  
這大概就是LitePal中連綴查詢的所有用法了。看出區別了吧?這種查詢的好處就在於,我們可以隨意地組合各種查詢參數,需要用到的時候就把它們連綴到一起,不需要用到的時候不用指定就可以了。對比一下query()方法中那冗長的參數列表,即使我們用不到那些參數,也必須要傳null,是不是明顯感覺LitePal中的查詢更加人性化?

激進查詢

不過,上述我們的所有用法中,都只能是查詢到指定表中的數據而已,關聯表中數據是無法查到的,因爲LitePal默認的模式就是懶查詢,當然這也是推薦的查詢方式。那麼,如果你真的非常想要一次性將關聯表中的數據也一起查詢出來,當然也是可以的,LitePal中也支持激進查詢的方式,下面我們就來一起看一下。

不知道你有沒有發現,剛纔我們所學的每一個類型的find()方法,都對應了一個帶有isEager參數的方法重載,這個參數相信大家一看就明白是什麼意思了,設置成true就表示激進查詢,這樣就會把關聯表中的數據一起查詢出來了。

比如說,我們想要查詢news表中id爲1的新聞,並且把這條新聞所對應的評論也一起查詢出來,就可以這樣寫:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. News news = DataSupport.find(News.class1true);  
  2. List<Comment> commentList = news.getCommentList();  
可以看到,這裏並沒有什麼複雜的用法,也就是在find()方法的最後多加了一個true參數,就表示使用激進查詢了。這會將和news表關聯的所有表中的數據也一起查出來,那麼comment表和news表是多對一的關聯,所以使用激進查詢一條新聞的時候,那麼該新聞所對應的評論也就一起被查詢出來了。

激進查詢的用法非常簡單,就只有這麼多,其它find()方法也都是同樣的用法,就不再重複介紹了。但是這種查詢方式LitePal並不推薦,因爲如果一旦關聯表中的數據很多,查詢速度可能就會非常慢。而且激進查詢只能查詢出指定表的關聯表數據,但是沒法繼續迭代查詢關聯表的關聯表數據。因此,這裏我建議大家還是使用默認的懶加載更加合適,至於如何查詢出關聯表中的數據,其實只需要在模型類中做一點小修改就可以了。修改News類中的代碼,如下所示:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. public class News extends DataSupport{  
  2.       
  3.     ...  
  4.   
  5.     public List<Comment> getComments() {  
  6.         return DataSupport.where("news_id = ?", String.valueOf(id)).find(Comment.class);  
  7.     }  
  8.       
  9. }  
可以看到,我們在News類中添加了一個getComments()方法,而這個方法的內部就是使用了一句連綴查詢,查出了當前這條新聞對應的所有評論。改成這種寫法之後,我們就可以將關聯表數據的查詢延遲,當我們需要去獲取新聞所對應的評論時,再去調用News的getComments()方法,這時纔會去查詢關聯數據。這種寫法會比激進查詢更加高效也更加合理。

原生查詢

相信你已經體會到,LitePal在查詢方面提供的API已經相當豐富了。但是,也許你總會遇到一些千奇百怪的需求,可能使用LitePal提供的查詢API無法完成這些需求。沒有關係,因爲即使使用了LitePal,你仍然可以使用原生的查詢方式(SQL語句)來去查詢數據。DataSuppport類中還提供了一個findBySQL()方法,使用這個方法就能通過原生的SQL語句方式來查詢數據了,如下所示:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. Cursor cursor = DataSupport.findBySQL("select * from news where commentcount>?""0");  
findBySQL()方法接收任意個字符串參數,其中第一個參數就是SQL語句,後面的參數都是用於替換SQL語句中的佔位符的,用法非常簡單。另外,findBySQL()方法返回的是一個Cursor對象,這和原生SQL語句的用法返回的結果也是相同的。

好了,這樣我們就把LitePal中提供的查詢數據的方法全部都學完了,那麼今天的文章就到這裏,下一篇文章當中會開始講解聚合函數的用法,感興趣的朋友請繼續閱讀 Android數據庫高手祕籍(八)——使用LitePal的聚合函數 。

LitePal開源項目地址:https://github.com/LitePalFramework/LitePal

第一時間獲得博客更新提醒,以及更多技術信息分享,歡迎關注我的微信公衆號,掃一掃下方二維碼或搜索微信號guolin_blog,即可關注。

發佈了22 篇原創文章 · 獲贊 9 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章