一、LitePal簡介
1、(新建項目LitePalTest)
正式接觸第一個開源庫---LitePalLitePal是一款開源的Android 數據庫框架,它采用了對象關系映射(ORM)的模式。
2、配置LitePal,編輯app/build.gradle文件,在dependencies閉包中添加如下內容:
dependencies {
? ? implementation fileTree(dir: 'libs', include: ['*.jar'])
? ? implementation 'com.android.support:appcompat-v7:26.1.0'
? ? implementation 'com.android.support.constraint:constraint-layout:1.0.2'
? ? testImplementation 'junit:junit:4.12'
? ? androidTestImplementation 'com.android.support.test:runner:1.0.1'
? ? androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
? ? compile 'org.litepal.android:core:1.4.1'
}
這樣我們就成功把LitePal成功引入當前項目中了,接下來需要配置litepal.xml文件。右擊app/src/main目錄,new一個
Directory,命名為assets,然后在assets目錄下,新建一個litepal.xml。編輯,添加代碼,如下:
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
? ? <dbname value = "BookStore222">
? ? </dbname>
? ? <version value = "1">
? ? </version>
? ? <list>
?????</list>
</litepal>
其中,<daname>標簽用于指定數據庫名,<version>標簽用于指定數據庫版本號,<list>標簽用于指定所有的映射模型。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
? ? package="com.example.litepaltest">
? ? <application
? ? ? ? android:name="org.litepal.LitePalApplication"
? ? ? ? android:allowBackup="true"
? ? ? ? android:icon="@mipmap/ic_launcher"
? ? ? ? android:label="@string/app_name"
? ? ? ? android:roundIcon="@mipmap/ic_launcher_round"
? ? ? ? android:supportsRtl="true"
? ? ? ? android:theme="@style/AppTheme">
? ? ? ? <activity android:name=".MainActivity">
? ? ? ? ? ? <intent-filter>
? ? ? ? ? ? ? ? <action android:name="android.intent.action.MAIN" />
? ? ? ? ? ? ? ? <category android:name="android.intent.category.LAUNCHER" />
? ? ? ? ? ? </intent-filter>
? ? ? ? </activity>
? ? </application>
</manifest>
我們項目的application配置為org.litepal.LitePalApplication,只有這樣才能讓LitePal的所有功能都可以正常
工作,關于application的作用,我們再行介紹。
二、創建和升級數據庫
LitPal采取的是對象關系映射(ORM)的模式,簡單說就是我們使用的編程語言是面向對象語言,而使用的數據庫是
關系型數據庫,那么將面向對象的語言和面向關系的數據庫之間建立一種映射關系,這就是對象關系映射。
使用LitePal,可以用面向對象的思維來實現功能,定義一個Book類,代碼如下:
package com.example.litepaltest;
/**
?* Created by ZHJ on 2018/3/4.
?*/
public class Book {
? ? private int id;
? ??
? ? private String author;
? ? ?
? ? private? double price;
? ??
? ? private? int pages;
? ??
? ? private String name;
? ? public int getId() {
? ? ? ? return id;
? ? }
? ? public String getAuthor() {
? ? ? ? return author;
? ? }
? ? public double getPrice() {
? ? ? ? return price;
? ? }
? ? public int getPages() {
? ? ? ? return pages;
? ? }
? ? public String getName() {
? ? ? ? return name;
? ? }
? ? public void setId(int id) {
? ? ? ? this.id = id;
? ? }
? ? public void setAuthor(String author) {
? ? ? ? this.author = author;
? ? }
? ? public void setPrice(double price) {
? ? ? ? this.price = price;
? ? }
? ? public void setPages(int pages) {
? ? ? ? this.pages = pages;
? ? }
? ? public void setName(String name) {
? ? ? ? this.name = name;
? ? }
}
這是一個典型的Java bean,在Book中我們定義了id,author,price,pages,name 這幾個字段,并生成相應的getter和setter
方法(先將類中的字段定義好,按下Alt+Insert,在彈出的對話框中選擇Getter和Setter,接著使用Shift將所有字段選中。就會自動生成相應的getter和setter方法。)
相信你已經猜到了,Book類就會對應數據庫中的Book表,而類中的每個字段分別對應了,表中的每個列。
接下來將Book類添加到映射模型列表中,修改litepal.xml中的代碼,如下:
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
? ? <dbname value = "BookStore222">
? ? </dbname>
? ? <version value = "1">
? ? </version>
? ? <list>
? ? ? ? <mapping class = "com.example.litepaltest.Book"></mapping>
? ? </list>
</litepal>
這里通過<mapping>標簽來聲明我們要配置的映射模型類,注意是使用完整的類名。不管多少模型類需要映射,
都使用同樣的方式配置在<list>標簽下。
所有工作已經完成了,進行任意的一次數據庫操作,BookStore222.db數據庫就會被自動創建出來。
修改MainActivity中的代碼,如下:
package com.example.litepaltest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import org.litepal.LitePal;
public class MainActivity extends AppCompatActivity {
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? Button createDatabase = (Button) findViewById(R.id.create_database);
? ? ? ? createDatabase.setOnClickListener(new View.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(View view) {
? ? ? ? ? ? ? ? LitePal.getDatabase();
? ? ? ? ? ? }
? ? ? ? });
? ? }
}
調用 LitePal.getDatabase()方法就是一次簡單的數據庫操作,只要點擊一下按鈕,數據就自動創建完成了。運行一下程序,
然后點擊Create database按鈕,接著通過adb shell (主要在啟動相應模擬器的情況下,在命令行輸入adb shell)查看一下數據庫創建情況。
注意:
activity_main.xml的布局代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/create_database"android:layout_width="match_parent"android:layout_height="wrap_content"android:text = "Create database"/><Buttonandroid:id="@+id/add_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:text = "Add data"/><Buttonandroid:id="@+id/update_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:text = "Updata data"/><Buttonandroid:id="@+id/delete_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:text = "Delete data"/><Buttonandroid:id="@+id/query_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:text = "Query data"/></LinearLayout>
運行程序后:
通過adb shell 查看一下數據庫創建情況,(使用android 6 模擬器,android 7 似乎不可以進入超級管理員狀態,也就不可以操作數據庫)
adb shell 進入;
cd? data/data/com.example.項目名(listpaltest)
cd? databases
ls
如下:(具體參考《第一行代碼》第二版P234)
可以看到,這里有3張表,其中android_metadata表仍然不用管,table_schema是LitePal內部使用的,我么也可以直接忽視,
book表是我們根據定義的Book類以及勒種的字段來自動生成的。
使用LitePal來升級數據庫非常容易,只需要將你想改的地方,直接將版本號加1就行了。
我們要向Book表中添加一個Press(出版社)列,直接修改Book類中的代碼,添加一個press字段即可。如下:
package com.example.litepaltest;/*** Created by ZHJ on 2018/3/4.*/public class Book {.....private String press;
.....public String getPress() {return press;}
....public void setPress(String press) {this.press = press;}
....}
如此同時,我們還想添加一張Category表,那么只需要新建一個Category類就可以了,代碼如下:
package com.example.litepaltest;/*** Created by ZHJ on 2018/3/4.*/public class Category {private int id;private String categoryName;private int categoryCode;public void setId(int id) {this.id = id;}public void setCategoryName(String categoryName) {this.categoryName = categoryName;}public void setCategoryCode(int categoryCode) {this.categoryCode = categoryCode;}
}
改完了我們想改的東西,只需要記得將版本號加1就行了。
這里還需要添加一個新的模型類,因此也需要將它添加到映射模型列表中,修改litepal.xm中的代碼,如下:
<?xml version="1.0" encoding="utf-8" ?>
<litepal><dbname value = "BookStore222"></dbname><version value = "2">//原來是1修改之后變為2</version><list><mapping class = "com.example.litepaltest.Book" <mapping clss = "com.example.litepaltest.Category"></mapping>//來聲明沃我們要配置的模型類,一定要使用完整的類名。不管有多少模型類需要映射,都要以同樣的方式配置在<lise>標簽下。</list></litepal>
現在重新運行一下程序,點擊Create database按鈕,查看一下最新的建表語句,
C:\Users\ZHJ>adb shell
root@generic_x86:/ # cd data/data/com.example.litepaltest
root@generic_x86:/data/data/com.example.litepaltest # cd databases
127|root@generic_x86:/data/data/com.example.litepaltest/databases # ls
BookStore222.db
BookStore222.db-journal
qlite3 BookStore222.db? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <
sqlite> .table
如下:
我們可以看到,book表中新增了一個press列,category表也創建成功了。當然,LitePal還自動幫我們做了一項非常重要的工作,就是保留之前表中的所有數據,這樣就不用擔心數據丟失的問題了。
三、使用LitePal添加數據
使用LitePal來添加數據。
1、創建出模型類的實例。
2、再將所有要存儲的數據設置好。
3、最后調用一下save()方法,就可以了。
開始動手實現,觀察現有的模型類,你會發現沒有繼承結構的。因為LiePal進行表管理操作時不需要模型類有任何的繼承結構,
但是進行CRUD操作時,就不行了。必須要繼承自DataSupport類才行。因此我們需要把繼承結構加上。修改Book類,代碼如下:
package com.example.litepaltest;import org.litepal.crud.DataSupport;/*** Created by ZHJ on 2018/3/4.*/public class Book extends DataSupport{private int id;private String author;private double price;private int pages;
......
}
接著我們向Book表中添加數據,修改MainActivity中的代碼,如下:
package com.example.litepaltest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;import org.litepal.LitePal;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button createDatabase = (Button) findViewById(R.id.create_database);createDatabase.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {LitePal.getDatabase();}}); Button addData = (Button) findViewById(R.id.add_data);addData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setName("The Da Vinci Code");book.setAuthor("Dan Brown");book.setPages(454);book.setPrice(16.96);book.setPress("Unknown");book.save();}});}
}
我們首先創建出一個Book的實例,然后調用Book類中的各種set方法對數據進行設置,最后再調用book.save()方法就能完成數據添加操作。這個save()方法是從DataSupport類中繼承來的,除了save()方法外,DataSupport類還給我們提供了豐富的CRUD方法。
現在運行程序,點擊Add data按鈕,此時數據應該已經添加成功了。我們打開數據庫看一下。
我們點了4次Add Data 按鈕,添加了4個。
四、使用LitePal更新數據庫
更新數據庫比添加數據稍微復雜一些,因為它的API接口比較多。
最簡單的一種更新方式,就是對已經存儲的對象重新設值,然后重新調用save()方法即可。
什么是已經存儲的對象?
對于LitePal來說,對象是否已經存儲就是根據調用model.isSaved()方法的結果來判斷的,返回true表示已經存儲,返回false就表示未存儲,那么什么情況下,會返回true,什么情況下,會返回false?實際上,只有兩種情況下model會被認為是已經存儲的對象。
????? ? 一種情況是已經調用過model.save()方法去添加數據了。此時model會被認為是已存儲的對象。
????? ? 另一種情況是model對象是通過LitePal提供的查詢API查出來的,由于是從數據庫中查到的對象,因此也會被認為是已已經存儲的對象。
我們先通過第一種情況來進行驗證。
首先修改MainActivity中的代碼,如下:
package com.example.litepaltest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;import org.litepal.LitePal;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button createDatabase = (Button) findViewById(R.id.create_database);createDatabase.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {LitePal.getDatabase();}});Button addData = (Button) findViewById(R.id.add_data);addData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setName("The Da Vinci Code");book.setAuthor("Dan Brown");book.setPages(454);book.setPrice(16.96);book.setPress("Unknown");book.save();}}); Button updataData = (Button)findViewById(R.id.update_data);updataData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setName("The Lost Symbol");book.setAuthor("Dan Brown");book.setPages(510);book.setPrice(19.95);book.setPress("Unkown");book.save();book.setPrice(10.99);book.save();}});}
}
在更新數據按鈕的點擊事件里面,我先是們通過上一小節學習的知識添加了一條Book數據,然后調用setPrice()方法將這本的價格進行了修改,之后再次使用了save()方法。此時LitePal會發現當前的Book對象是存儲的,因此不會再向數據庫中添加一條新數據,而是直接會更新當前數據。
運行程序,點擊Update data 按鈕。輸入 select * from Book;
我們點了兩次Update data 按鈕,所有會更新兩個數據。
但是這種更新方式只能對已存儲的對象進行操作,限制比較大,我們學習另一種更新方式,修改MainActivity中的代碼,如下所示:
package com.example.litepaltest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;import org.litepal.LitePal;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button createDatabase = (Button) findViewById(R.id.create_database);createDatabase.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {LitePal.getDatabase();}});Button addData = (Button) findViewById(R.id.add_data);addData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setName("The Da Vinci Code");book.setAuthor("Dan Brown");book.setPages(454);book.setPrice(16.96);book.setPress("Unknown");book.save();}});Button updataData = (Button)findViewById(R.id.update_data);updataData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setPrice(14.95);book.setPress("Anchor");book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");}});}
}
在這里,我們首先new出了一個Book的實例,然后直接調用setPrice()和setPress()方法來設置要更新的數據,最后再調用updataAll()方法去執行更新操作。注意:updataAll()方法中可以指定一個條件約束,和SQLiteDatabase中的update()方法的where參數部分有點類似,但更加簡潔,如果不指定條件語句,就表示更新所有數據。這里我們指定將所有書名是The Lost Symbol 并且作者是Dan Brown 書價格更新為14.95,出版社更新為Anchor。
點擊Update data按鈕,再次查詢表中的數據情況:
可見第二本書的幾個被更新成了14.95,出版社被更新成了Anchor。
在使用updateAll()方法時,還有一個非常重要的知識點,就是當你想把一個字段的值更新為默認值時,是不可以使用上面的方式來set數據的。JAVA中任何一種數據類型都會有默認值。對于所有想要將數據更新為默認值的操作,LitePal統一提供了一個setToDefault()方法,然后傳入相應的列名就可以了,比如:
Book book = new Book();
book.setToDefault("pages");
book.updateAll("name =? and author","The Lost Symbol","Dan Brown");
這段代碼意思是將書名是The Lost Symbol,并且做作者是Dan Brown的頁數設置為默認值(就是0)。
package com.example.litepaltest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;import org.litepal.LitePal;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button createDatabase = (Button) findViewById(R.id.create_database);createDatabase.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {LitePal.getDatabase();}});Button addData = (Button) findViewById(R.id.add_data);addData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setName("The Da Vinci Code");book.setAuthor("Dan Brown");book.setPages(454);book.setPrice(16.96);book.setPress("Unknown");book.save();}});Button updataData = (Button)findViewById(R.id.update_data);updataData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) { Book book = new Book();book.setToDefault("pages");book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");}});}
}
下面來檢驗一下:
如上觀看;
因為updateAll()方法中制定了約束條件,若沒有約束條件,此次更新操作會對所有的數據生效。
五、使用LitePal刪除數據
使用LitePal刪除數據的方式主要有兩種,第一種比較簡單,就是直接調用已存儲對象的delete()方法就可以。所謂已存儲的對象的含義我們再說一遍,就是調用過save()方法的對象,或者是通過LitePal提供的查詢API查詢出來的對象,都是可以直接使用delete()方法來刪除對數據的。這種方式我們就不演示了。
我們來看另一種刪除數據的方式。
修改MainActivity中的代碼,如下:
package com.example.litepaltest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;import org.litepal.LitePal;
import org.litepal.crud.DataSupport;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button createDatabase = (Button) findViewById(R.id.create_database);createDatabase.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {LitePal.getDatabase();}});Button addData = (Button) findViewById(R.id.add_data);addData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setName("The Da Vinci Code");book.setAuthor("Dan Brown");book.setPages(454);book.setPrice(16.96);book.setPress("Unknown");book.save();}});Button updataData = (Button)findViewById(R.id.update_data);updataData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setToDefault("pages");book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");}});Button deleteButton = (Button)findViewById(R.id.delete_data);deleteButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {DataSupport.deleteAll(Book.class,"price<?","15");}});}
}
這里調用了DataSupport.deleteAll()方法來刪除數據,其中deleteAll()方法的第一個參數用來指定刪除哪一張表中的數據,Book.class意味著刪除Book表中的數據。后面的語句用于指定約束條件。這行代碼的意思就是刪除price小于15的書。
運行程序,點擊按鈕,查詢表中數據,如下:
可以看到低于15的書的數據已經被刪除了。
另外,deleteAll()方法,如果不指定約束條件,就意味著要刪除表中所有的數據和updateAll()方法相似。
六、使用LitePal查詢數據
使用LitePal來查詢數據一點都不復雜,LitePal在查詢API方面的設計及其人性化,基本上可以滿足絕大多數場景的查詢需求,并且代碼十分整潔。比如我們打算查詢表中的所有數據,使用LitePal如何完成同樣的功能呢?
只需這樣寫:
List<Book> books = DataSupport.findAll(Book.class);
只需要調用一下findAll()方法的返回值是一個Book類型的List集合,LitePal已經自動幫我們完成了賦值操作。
修改MainActivity中的代碼,如下:
package com.example.litepaltest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;import org.litepal.LitePal;
import org.litepal.crud.DataSupport;import java.util.List;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button createDatabase = (Button) findViewById(R.id.create_database);createDatabase.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {LitePal.getDatabase();}});Button addData = (Button) findViewById(R.id.add_data);addData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setName("The Da Vinci Code");book.setAuthor("Dan Brown");book.setPages(454);book.setPrice(16.96);book.setPress("Unknown");book.save();}});Button updataData = (Button)findViewById(R.id.update_data);updataData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Book book = new Book();book.setToDefault("pages");book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");}});Button deleteButton = (Button)findViewById(R.id.delete_data);deleteButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {DataSupport.deleteAll(Book.class,"price<?","15");}});Button queryButton = (Button)findViewById(R.id.query_data);queryButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {List<Book> books = DataSupport.findAll(Book.class);for(Book book: books){Log.d("MainActivity","book name is "+"book.getName");Log.d("MainActivity","book author is "+"book.getAuthor");Log.d("MainActivity","book pages is "+"book.getPages");Log.d("MainActivity","book price is "+"book.getPrice");Log.d("MainActivity","book press is "+"book.getPress");}}});}
}
遍歷List集合中的Book對象,并將其中的信息全部打印出來。現在運行一下程序,點擊Query data按鈕。查看logcat的打印內容。
這樣,我們就已經將這條數據查詢出了。
注意事項:
以上,我們是基于大牛郭霖的《第一行代碼》系列書籍中的數據庫內容,進行的解釋。再一次從內心中迸發出的自由而不尷尬的崇高敬意。
此外,我們還可以通過操作把輸入框中輸入的數據,通過按鈕傳入到SQLite數據庫,以后,我們會進行詳細解釋。