Android数据库框架LitePal详解
LitPal
1.簡介
LitePal是開源的Android數據庫框架,采用了對象關系映射(ORM)的模式,并將我們平時開發最常用到的一些數據庫功能進行了封裝,使得不用編寫一行SQL語句就可以完成建表和增刪改查。
?
2.配置LitePal
2.1 把litepal引入到項目中
dependencies{
compile 'org.litepal.android:core:1.6.1'
}
?
2.2配置litepal.xml文件
右擊app/src/main目錄--new--Directory,創建一個assets目錄,然后在assets目錄下再創建一個litpal.xml文件,內容如下
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="CourseManager" />
<version value="1" />
<list>
。。。
</list>
</litepal>
其中,<dbname>標簽用于指定數據庫名,<version>用于指定數據庫版本號,<list>用于指定所有的映射模型。
?
2.3修改AndroidManifest.xml文件
<application
android:mane="org.;itepal.LitePalApplication"
...
</application>
是litePal的所有功能都可以正常工作。擴展看十三章
?
3.創建數據庫
將面向對象的語言和面向關系的數據庫之間建立一種映射關系。
3.1創建一個實體類例如 AbnormalCrew.class
public class AbnormalCrew {
private String picture;
private String crewName;
private String crewCode;
private String gender;
private String reason;
private String crewId;
private String country;
?
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getCrewId() {
return crewId;
}
public void setCrewId(String crewId) {
this.crewId = crewId;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
public String getCrewName() {
return crewName;
}
public void setCrewName(String crewName) {
this.crewName = crewName;
}
public String getCrewCode() {
return crewCode;
}
public void setCrewCode(String crewCode) {
this.crewCode = crewCode;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
?
3.2 將 AbnormalCrew類添加到映射模型立標中
修改litepal.xm文件:放在<list>標簽下
<dbname value="Crew"></dbname>
<list>
<mapping class = "com.itiis.whut.certificateaudit.beans.AbnormalCrew"/>
......
</list>
3.3 在XXXActivity中添加代碼:
獲取數據庫 ,必須使用否則報空錯誤 ,放在application或第一個啟動的activity中都可以。
SQLiteDatabase db = LitePal.getDatabase();
調用getDatabase就可以創建數據庫和對一個的表。
?
4.升級數據庫
表加列,新建表。。。。。
?
4.1 比如在AbnormalCrew表中添加一個age列:
public class AbnormalCrew {
....
private String sex;
?
...getAge(){
...
}
...setAge(){
...
}
?
}
新建一個表Crew:
新建過程省略,上面有。。。
?
4.2 修改litpal.xml文件
?
?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="CourseManager" />
<version value="2" />
<list>
<mapping class = "com.itiis.whut.certificateaudit.beans.AbnormalCrew"/>
<mapping class = "com.itiis.whut.certificateaudit.beans.Crew"/>
</list>
</litepal>
記得版本號加1.
?
5.是用litepal添加數據
例如向AbnormalCrew添加數據。
5.1讓 AbnormalCrew繼承DataSupport
因為要進行CRUD操作,必須繼承DataSupport類。
public class AbnormalCrew extends DataSupport{
...
}
?
5.2添加數據
創建出模型類的實例,用set方法設置數據,最后調用save()方法。
在xxxActivity中修改代碼:
AbnormalCrew abnormalCrew = new AbnormalCrew();
abnormalCrew.setCrewName("jmq");
........
abnormalCrew.save();
?
save()方法是DataSupport類里面的。
?
6.更新數據
?
6.1方法一:
對已存儲對象重新設值,然后重新調用save()方法即可。
什么是已存儲對象(經過持久化)?怎么判斷?
兩種情況:
第一種:已經調用過model.save()方法去添加數據的,此時model會被認為是已存儲的對象。
第二種:model對象是通過LitePal提供的查詢API查出來的,由于是從數據庫中查找對象。因此也會被認為是已存儲的對象。
判斷:
通過model.isSaved()方法,返回true則已存儲,false表示未存儲。
?
AbnormalCrew abnormalCrew = new AbnormalCrew();
abnormalCrew.setCrewName("jmq");
........
abnormalCrew.save();
abnormalCrew.setCrewName("小明");
abnormalCrew.save();
?
這樣可以對CrewName的值進行修改。
?
6.2方法二:
是用updata()方法
public static int update( long id)
這個靜態的update()方法接收參數是一個指定的id,表示我們要修改哪一行數據。
?
AbnormalCrew abnormalCrew = new AbnormalCrew();
abnormalCrew.setCrewName("小明");
abnormalCrew.update(2);
在指定修改某一條id記錄的時候只需要傳入這個id即可,語法更簡練。
?
6.3方法三
也許我想修改的是某一個條件下的所有數據,而不是僅僅修改某個id的數據,那該怎么辦呢?LitePal還提供了另外一個簡便的方法,
AbnormalCrew abnormalCrew = new AbnormalCrew();
abnormalCrew.setCrewName("小明");
abnormalCrew.updateAll("gender = ? and country > ?", "男", "中國");
?
7.使用LitePal刪除數據
7.1是用delete()方法
就是直接調用已存儲對象的delete()就可以了。
7.2調用deleteAll()方法
?
LitePal刪除數據的API和修改數據是比較類似的,但是更加的簡單一些,我們先來看一下DataSupport類中的方法定義,如下所示:
public static int delete(Class<?> modelClass, long id)
delete()方法接收兩個參數,第一個參數是Class,傳入我們要刪除的那個類的Class就好,第二個參數是一個指定的id,表示我們要刪除哪一行數據。
那么比如說我們想刪除news表中id為2的記錄,就可以這樣寫:
DataSupport.delete(News.class, 2);
需要注意的是,這不僅僅會將news表中id為2的記錄刪除,同時還會將其它表中以news id為2的這條記錄作為外鍵的數據一起刪除掉,因為外鍵既然不存在了,那么這么數據也就沒有保留的意義了。
說起來可能有點拗口,我們還是舉例看一下。比如news表中目前有兩條數據,如下圖所示:
?
?
然后comment表中也有兩條數據,如下圖所示:
?
其中comment表中兩條數據的外鍵都是2,指向的news表中id為2的這條記錄。那么下面我們執行如下刪除語句:
int deleteCount = DataSupport.delete(News.class, 2);
Log.d("TAG", "delete count is " + deleteCount);
其中delete()方法的返回值表示被刪除的記錄數,打印結果如下所示:
?
?
可以看到,有三條記錄被刪除了,那我們再到news表中查詢一下:
?
?
OK,只剩下一條記錄了,id為2的那條記錄確實被刪除了。那么再到comment表中看一下呢,如下圖所示:
?
?
數據全沒了!為什么呢?因為comment表中的兩條數據都是以news表中id為2的數據作為外鍵的,現在外鍵不存在了,那么這兩條數據自然也沒有存在的意義了,因此被刪除的記錄數一共是3條。這樣是不是就好理解了很多呢?
?
?
?
除了刪除指定id的數據之外,DataSupport中也提供了一個通過where語句來批量刪除數據的方法,先看一下方法定義:
?
public static int deleteAll(Class<?> modelClass, String... conditions)
看起來很眼熟吧?非常簡單,deleteAll()方法接收兩個參數,第一個參數是Class,傳入我們要刪除的那個類的Class就好,第二個參數是一個conditions數組,用于指定刪除哪些行的約束條件,返回值表示此次刪除了多少行數據,用法和updateAll()方法是基本相同的。
?
?
?
那么比如說我們想把news表中標題為“今日iPhone6發布”且評論數等于0的所有新聞都刪除掉,就可以這樣寫:
?
DataSupport.deleteAll(News.class, "title = ? and commentcount = ?", "今日iPhone6發布", "0");
而如果我們想把news表中所有的數據全部刪除掉,就可以這樣寫:
DataSupport.deleteAll(News.class);
在不指定約束條件的情況下,deleteAll()方法就會刪除表中所有的數據了。
?
?
?
除了DataSupport類中提供的靜態刪除方法之外,還有一個刪除方法是作用于對象上的,即任何一個繼承自DataSupport類的實例都可以通過調用delete()這個實例方法來刪除數據。但前提是這個對象一定是要持久化之后的,一個非持久化的對象如果調用了delete()方法則不會產生任何效果。
?
?
?
比如說下面這種寫法:
?
News news = new News();
news.delete();
這里new出了一個News對象,這個對象明顯是沒有持久化的,那么此時調用delete()方法則不會刪除任何數據。
?
?
?
但如果我們之前將這個對象持久化過了,那么再調用delete()方法就會把這個對象對應的數據刪除掉了,比如:
?
News news = new News();
news.setTitle("這是一條新聞標題");
news.setContent("這是一條新聞內容");
news.save();
...
news.delete();
一個對象如果save過了之后,那就是持久化的了。除了調用save()方法之外,通過DataSupport中提供的查詢方法從數據庫中查出來的對象也是經過持久化的,查詢的功能我們會在下篇博客中講解。
?
?
?
另外還有一個簡單的辦法可以幫助我們判斷一個對象是否是持久化之后的,DataSupport類中提供了一個isSaved()方法,這個方法返回true就表示該對象是經過持久化的,返回false則表示該對象未經過持久化。那么刪除一個對象對應的數據也就可以這樣寫了:
?
News news;
...
if (news.isSaved()) {
news.delete();
}
好了,這樣我們就把LitePal中提供的修改和刪除數據操作的用法基本都學習完了,那么今天的文章就到這里,
?
使用LitePal查詢數據
?
?
LitePal在查詢方面提供了非常豐富的API,功能多種多樣,基本上已經能夠滿足我們平時所有的查詢需求了。不僅如此,LitePal在查詢API的設計方面也是非常用心,摒棄了原生query()方法中繁瑣的參數列表,而是改用了一種更為靈巧的方式——連綴查詢。除此之外,LitePal查詢的結果也不再返回Cursor對象,然后再由開發者自己去逐個取出,而是直接返回封裝好的對象。這些改變都使得查詢數據變得更加簡單,也更加合理,那么下面我們就來完整地學習一下LitePal中查詢數據的所有用法。
?
?
?
簡單查詢
?
?
?
比如說現在我們想實現一個最簡單的功能,查詢news表中id為1的這條記錄,使用LitePal就可以這樣寫:
?
News news = DataSupport.find(News.class, 1);
天吶!有沒有覺得太輕松了?僅僅一行代碼,就可以把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表中的第一條數據,只需要這樣寫:
?
News firstNews = DataSupport.findFirst(News.class);
OK,語義性非常強吧,讓人一眼就看懂是什么意思了,只需調用findFirst()方法,然后傳入News類,得到的就是news表中的第一條數據了。
?
?
?
那我們舉一翻三,如果是想要獲取News表中的最后一條數據該怎么寫呢?同樣簡單,如下所示:
?
News lastNews = DataSupport.findLast(News.class);
因為獲取表中第一條或者是最后一條數據的場景比較常見,所以LitePal特意提供了這兩個方法來方便我們的操作。
?
?
?
那么我們看到這里,目前都只是查詢單條數據的功能,如果想要查詢多條數據該怎么辦呢?比如說,我們想把news表中id為1、3、5、7的數據都查出來,該怎么寫呢?也許有的朋友會比較聰明,立馬就想到可以一個個去查,就調用四次find()方法嘛,然后把1、3、5、7這四個id分別傳進去不就可以了。沒錯,這樣做完全是可以的,而且效率也并不低,但是LitePal給我們提供了一個更簡便的方法——findAll()。這個方法的用法和find()方法是非常類似的,只不過它可以指定多個id,并且返回值也不再是一個泛型類對象,而是一個泛型類集合,如下所示:
?
List<News> newsList = DataSupport.findAll(News.class, 1, 3, 5, 7);
可以看到,首先我們是調用的findAll()方法,然后這個方法的第一個參數仍然是指定的泛型類,但是后面的參數就很隨意了,你可以傳入任意個id進去,findAll()方法會把所有傳入的id所對應的數據全部查出來,然后一起返回到List<News>這個泛型集合當中。
?
?
?
雖說這個語法設計算是相當人性化,但是在有些場景或許不太適用,因為可能要你要查詢的多個id已經封裝到一個數組里了。那么沒關系,findAll()方法也是接收數組參數的,所以說同樣的功能你也可以這樣寫:
?
long[] ids = new long[] { 1, 3, 5, 7 };
List<News> newsList = DataSupport.findAll(News.class, ids);
看到這里,那有的朋友可能會奇怪了,說findAll()方法不應該是查詢所有數據的意思嗎?怎么總是查詢幾個id所對應數據呢?哈!這個問題問得好,因為findAll()方法也是可以查詢所有數據的,而且查詢所有數據的寫法更簡單,只需要這樣寫:
List<News> allNews = DataSupport.findAll(News.class);
看到沒有,我們只需要把后面的參數都去掉,在不指定具體id的情況下,findAll()方法查詢出的就是news表中的所有數據了,是不是語義性非常強?
?
?
?
而且大家不要以為剛才這些都只是findAll()的幾個方法重載而已,實際上剛才我們的這幾種用法都是調用的同一個findAll()方法!一個方法卻能夠實現多種不同的查詢效果,并且語義性也很強,讓人一看就能理解,這就是LitePal的查詢藝術!
?
?
?
連綴查詢
?
?
?
當然了,LitePal給我們提供的查詢功能還遠遠不只這些,好戲還在后頭。相信大家現在也已經發現了,我們目前的查詢功能都是基于id來進行查詢的,并不能隨意地指定查詢條件。那么怎樣才能指定查詢條件呢?讓我們回想一下傳統情況應該怎么做,query()方法中接收七個參數,其中第三和第四個參數就是用于指定查詢條件的,然后其它幾個參數都填null就可以了。但是呢,前面我們已經痛批過了這種寫法,因為冗長的參數列表太過繁瑣,那么LitePal又是怎么解決這個問題的呢?我們現在就來學習一下。
?
?
?
為了避免冗長的參數列表,LitePal采用了一種非常巧妙的解決方案,叫作連綴查詢,這種查詢很靈活,可以根據我們實際的查詢需求來動態配置查詢參數。 那這里舉個簡單的例子,比如我們想查詢news表中所有評論數大于零的新聞,就可以這樣寫:
?
List<News> newsList = DataSupport.where("commentcount > ?", "0").find(News.class);
可以看到,首先是調用了DataSupport的where()方法,在這里指定了查詢條件。where()方法接收任意個字符串參數,其中第一個參數用于進行條件約束,從第二個參數開始,都是用于替換第一個參數中的占位符的。那這個where()方法就對應了一條SQL語句中的where部分。
?
?
?
接著我們在where()方法之后直接連綴了一個find()方法,然后在這里指定一個泛型類,表示用于查詢哪張表。那么上面的一段代碼,查詢出的結果和如下SQL語句是相同的:
?
select * from users where commentcount > 0;
但是這樣會將news表中所有的列都查詢出來,也許你并不需要那么多的數據,而是只要title和content這兩列數據。那么也很簡單,我們只要再增加一個連綴就行了,如下所示:
?
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0").find(News.class);
可以看到,這里我們新增了一個select()方法,這個方法接收任意個字符串參數,每個參數要求對應一個列名,這樣就只會把相應列的數據查詢出來了,因此select()方法對應了一條SQL語句中的select部分。
?
?
?
那么上面的一段代碼,查詢出的結果和如下SQL語句是相同的:
?
select title,content from users where commentcount > 0;
很好玩吧?不過這還不算完呢,我們還可以繼續連綴更多的東西。比如說,我希望將查詢出的新聞按照發布的時間倒序排列,即最新發布的新聞放在最前面,那就可以這樣寫:
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc").find(News.class);
order()方法中接收一個字符串參數,用于指定查詢出的結果按照哪一列進行排序,asc表示正序排序,desc表示倒序排序,因此order()方法對應了一條SQL語句中的order by部分。
?
?
那么上面的一段代碼,查詢出的結果和如下SQL語句是相同的:
?
select title,content from users where commentcount > 0 order by publishdate desc;
然后呢,也許你并不希望將所有條件匹配的結果一次性全部查詢出來,因為這樣數據量可能會有點太大了,而是希望只查詢出前10條數據,那么使用連綴同樣可以輕松解決這個問題,代碼如下所示:
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc").limit(10).find(News.class);
這里我們又連綴了一個limit()方法,這個方法接收一個整型參數,用于指定查詢前幾條數據,這里指定成10,意思就是查詢所有匹配結果中的前10條數據。
?
?
那么上面的一段代碼,查詢出的結果和如下SQL語句是相同的:
?
select title,content from users where commentcount > 0 order by publishdate desc limit 10;
剛才我們查詢到的是所有匹配條件的前10條新聞,那么現在我想對新聞進行分頁展示,翻到第二頁時,展示第11到第20條新聞,這又該怎么實現呢?沒關系,在LitePal的幫助下,這些功能都是十分簡單的,只需要再連綴一個偏移量就可以了,如下所示:
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc").limit(10).offset(10)
.find(News.class);
可以看到,這里我們又添加了一個offset()方法,用于指定查詢結果的偏移量,這里指定成10,就表示偏移十個位置,那么原來是查詢前10條新聞的,偏移了十個位置之后,就變成了查詢第11到第20條新聞了,如果偏移量是20,那就表示查詢第21到第30條新聞,以此類推。因此,limit()方法和offset()方法共同對應了一條SQL語句中的limit部分。
?
?
那么上面的一段代碼,查詢出的結果和如下SQL語句是相同的:
?
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的新聞,并且把這條新聞所對應的評論也一起查詢出來,就可以這樣寫:
?
News news = DataSupport.find(News.class, 1, true);
List<Comment> commentList = news.getCommentList();
可以看到,這里并沒有什么復雜的用法,也就是在find()方法的最后多加了一個true參數,就表示使用激進查詢了。這會將和news表關聯的所有表中的數據也一起查出來,那么comment表和news表是多對一的關聯,所以使用激進查詢一條新聞的時候,那么該新聞所對應的評論也就一起被查詢出來了。
?
?
激進查詢的用法非常簡單,就只有這么多,其它find()方法也都是同樣的用法,就不再重復介紹了。但是這種查詢方式LitePal并不推薦,因為如果一旦關聯表中的數據很多,查詢速度可能就會非常慢。而且激進查詢只能查詢出指定表的關聯表數據,但是沒法繼續迭代查詢關聯表的關聯表數據。因此,這里我建議大家還是使用默認的懶加載更加合適,至于如何查詢出關聯表中的數據,其實只需要在模型類中做一點小修改就可以了。修改News類中的代碼,如下所示:
?
public class News extends DataSupport{
...
public List<Comment> getComments() {
return DataSupport.where("news_id = ?", String.valueOf(id)).find(Comment.class);
}
}
可以看到,我們在News類中添加了一個getComments()方法,而這個方法的內部就是使用了一句連綴查詢,查出了當前這條新聞對應的所有評論。改成這種寫法之后,我們就可以將關聯表數據的查詢延遲,當我們需要去獲取新聞所對應的評論時,再去調用News的getComments()方法,這時才會去查詢關聯數據。這種寫法會比激進查詢更加高效也更加合理。
?
?
?
原生查詢
?
?
?
相信你已經體會到,LitePal在查詢方面提供的API已經相當豐富了。但是,也許你總會遇到一些千奇百怪的需求,可能使用LitePal提供的查詢API無法完成這些需求。沒有關系,因為即使使用了LitePal,你仍然可以使用原生的查詢方式(SQL語句)來去查詢數據。DataSuppport類中還提供了一個findBySQL()方法,使用這個方法就能通過原生的SQL語句方式來查詢數據了,如下所示:
?
Cursor cursor = DataSupport.findBySQL("select * from news where commentcount>?", "0");
findBySQL()方法接收任意個字符串參數,其中第一個參數就是SQL語句,后面的參數都是用于替換SQL語句中的占位符的,用法非常簡單。另外,findBySQL()方法返回的是一個Cursor對象,這和原生SQL語句的用法返回的結果也是相同的。
?
?
?
好了,這樣我們就把LitePal中提供的查詢數據的方法全部都學完了,那么今天的文章就到這里
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的Android数据库框架LitePal详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员,当你写程序写累了怎么办。
- 下一篇: 灰色的生命