持久化技術之SQLite 數據庫存儲
一、SQLite數據庫
SQLite是一款輕量級的關係型數據庫,運算速度非常快,佔用資源少。支持標準的SQL語法,遵循數據庫的ACID事務。不用設置用戶名和密碼即可使用。
二、SQLiteOpenHelper幫助類
- 這是一個抽象類,使用需要創建一個類繼承它,並重寫兩個抽象方法(onCreate(…) && onUpgrade(…)),在這兩個方法中去實現創建、升級數據庫的邏輯。
- 兩個重要方法:getReadableDatabase() 和 getWritableDatabase()
這兩個方法都可以創建或打開一個現有的數據庫,並返回一個可對數據庫進行讀寫操作的對象。 - 兩個構造函數,一般使用參數少的那個。
public 類名 (…) 四個參數:
參數一:Context;
參數二:(String)數據庫名;
參數三:允許查詢數據的時候返回一個自定義的Cursor,一般傳入null;
參數四:當前數據庫的版本號。 - 數據庫文件會存放在/data/data//databases/目錄下
- 使用 adb shell 來對數據庫和表的創建情況進行檢查。
三、對數據庫進行CRUD操作
C添加(Create)R查詢(Retrieve)U更新(Update)D刪除(Delete)
使用 getReadableDatabase() 和 getWritableDatabase() 返回的 SQLiteDatabase 對象對數據進行CRUD操作。
1. 添加數據 insert(…)方法:3個參數
參數一:表名;
參數二:在未指定添加數據的情況下給某些可爲空的列自動賦值NULL;
參數三:ContentValues對象 提供一系列put(列名,數據)方法重載。
2. 更新數據 update(…)方法:4個參數
參數一:表名;
參數二:ContentValues對象 提供一系列put(列名,數據)方法重載;將要修改的數據放入其中;
參數三:對應於SQL語句的WHERE部分,這裏表示更新所有name等於?的行?是一個佔位符,通過參數四賦值;
參數四:字符串數組 來指定相應內容。
第三、四個參數用於約束更新某一行或某幾行中的數據,不指定的話默認更新所有行。
3. 刪除數據 delete(…)方法:三個參數
參數一:表名;
參數二:WHERE條件;
參數三:字符串數組。
第二、三個參數用於約束刪除某一行或某幾行中的數據,不指定的話默認刪除所有行。
4. 查詢數據 query(…)方法:七個參數
參數一:表名;
參數二:用於查詢哪幾列,不指定則默認查詢所有列;
第三(WHERE)、四(爲佔位符提供具體的值)個參數用於約束查詢某一行或某幾行中的數據,不指定的話默認查詢所有行;
參數五:用於指定需要group by的列,不指定則表示不對查詢結果進行group by操作;
參數六:對group by 之後的數據進行進一步的過濾,不指定則表示不過濾;
參數七:對查詢結果指定排序方式,不指定則表示使用默認的排序方式。
四、Demo 代碼
1. 效果界面:
2. 具體代碼:
A.)activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.thinkpad.databasetest.MainActivity">
<!--創建數據表按鈕-->
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"/>
<!--添加數據按鈕-->
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add data"/>
<!--更新數據按鈕-->
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update data"/>
<!--刪除數據按鈕-->
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete data"/>
<!--查詢數據按鈕-->
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query data"/>
<!--用於顯示數據庫的查詢結果-->
<EditText
android:id="@+id/show_query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
B.)MyDatabaseHelper.java
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
public class MyDatabaseHelper extends SQLiteOpenHelper {
//建表語句1
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement,"
+ "author text,"
+ "price real,"
+ "pages inteegr,"
+ "name text)";
//建表語句2
public static final String CREATE_CATEGORY = "create table Category("
+ "id integer primary key autoincrement,"
+ "category_name text,"
+ "category_code integer)";
private Context mContext;
//構造器 參數一:上下文;參數二:數據庫名;參數三:自定義的Cursor,一般爲null;參數四:數據庫的版本號
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,int version){
super(context,name,factory,version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext,"Create succeeded",Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
C.)MainActivity.java
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);
//創建表
Button createBase = (Button)findViewById(R.id.create_database);
createBase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dbHelper.getWritableDatabase();
}
});
//添加數據
Button addData = (Button)findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
//開始組裝第一條數據
values.put("name","The Da Vinci Code");
values.put("author","Dan Brown");
values.put("pages",454);
values.put("price",16.96);
//插入第一條數據
//參數一:表名;
//參數二:在未指定添加數據的情況下給某些可爲空的列自動賦值NULL;
//參數三:ContentValues對象 提供一系列put(列名,數據)方法重載
db.insert("Book",null,values);
//開始組裝第二條數據
values.put("name","The Lost Symbol");
values.put("author","Dan Brown");
values.put("pages",510);
values.put("price",19.95);
//插入第二條數據
db.insert("Book",null,values);
}
});
//更新數據 將名字爲The Da Vinci Code 的這本書的價格改爲10.99
Button updateButton = (Button)findViewById(R.id.update_data);
updateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price",10.99);
//更新數據
//參數一:表名;
//參數二:ContentValues對象 提供一系列put(列名,數據)方法重載;將要修改的數據放入其中;
//參數三:對應於SQL語句的WHERE部分,這裏表示更新所有name等於?的行
//?是一個佔位符,通過參數四:字符串數組 來指定相應內容。
//第三、四個參數用於約束更新某一行或某幾行中的數據,不指定的話默認更新所有行
db.update("Book",values,"name=?",new String[]{"The Da Vinci Code"});
}
});
//刪除數據
//參數一:表名;
//參數二:WHERE條件;
//參數三:字符串數組;
//第二、三個參數用於約束刪除某一行或某幾行中的數據,不指定的話默認刪除所有行
Button deleteButton = (Button)findViewById(R.id.delete_data);
updateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book","pages > ? ",new String[]{"500"});
}
});
//查詢數據
//參數一:表名;
//參數二:用於查詢哪幾列,不指定則默認查詢所有列;
//第三(WHERE)、四(爲佔位符提供具體的值)個參數用於約束查詢某一行或某幾行中的數據,不指定的話默認查詢所有行;
//參數五:用於指定需要group by的列,不指定則表示不對查詢結果進行group by操作;
//參數六:對group by 之後的數據進行進一步的過濾,不指定則表示不過濾;
//參數七:對查詢結果指定排序方式,不指定則表示使用默認的排序方式。
Button queryButton = (Button)findViewById(R.id.query_data);
queryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
//查詢Book表中所有數據
Cursor cursor = db.query("Book",null,null,null,null,null,null);
TextView showQueryText = (TextView)findViewById(R.id.show_query_data);
if(cursor.moveToFirst()){
do{
//遍歷Cursor對象,取出數據並打印
//先通過列名得出索引值,根據索引值獲得相應列下的屬性值
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d("MainActivity","book name is" + name);
Log.d("MainActivity","book author is" + author);
Log.d("MainActivity","book pages is" + pages);
Log.d("MainActivity","book price is" + price);
String text = name + " " + author;
showQueryText.setText(text);
}while(cursor.moveToNext());
}
cursor.close();
}
});
}
}
五、使用 LitePal 操作數據庫
LitePal 是一款開源得到Android數據庫框架,採用對象關係映射(ORM)的模式,將一些常用的數據庫功能進行了封裝。
使用文檔:https://github.com/LitePalFramework/LitePal
1. 基本配置說明:
A.)編輯app/build.gradle文件,在dependencies閉包中添加:
implementation 'org.litepal.android:core:3.0.0'
固定部分:implementation ‘org.litepal.android:core:’
變動部分:3.0.0 版本號
B.)在main目錄下新建目錄assets,再在這個目錄下新建文件litepal.xml
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
<dbname value="BookStore"></dbname><!--用於指定數據庫的名稱-->
<version value="1"></version><!--用於指定數據庫的版本號-->
<list></list><!--用於指定所有的映射模型-->
</litepal>
C.)配置項目的application(AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.thinkpad.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>
2. 對象關係映射
將面向對象的語言和麪向關係的數據庫之間建立一種映射關係。
3. 創建數據庫
A.)新建Book類
//Book類會對應數據庫中的Book表
//類中的每一個字段對應表中的每一個列
public class Book {
private int id;
private String author;
private double price;
private int pages;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
B.)將Book類添加到映射模型列表當中(litepal.xml)
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
<dbname value="BookStore"></dbname><!--用於指定數據庫的名稱-->
<version value="1"></version><!--用於指定數據庫的版本號-->
<list>
<mapping class="com.example.thinkpad.litepaltest.Book"></mapping>
<!--使用<mapping>標籤來聲明我們要配置的映射模型類,注意一定要使用完整的類名-->
</list><!--用於指定所有的映射模型-->
</litepal>
C.)只要進行任意一次數據庫的操作,BookStore.db數據庫就會自動創建出來。在主函數的按鈕的點擊事件中,寫一個最簡單的數據庫操作:Connector.getDatabase();點擊按鈕,數據庫創建成功。
4. 升級數據庫
修改想修改的內容,並且將數據庫版本號+1即可。
想要給Book表添加一列,則直接修改Book.java在其中添加一個字段,並且設置它的get和set方法,版本號+1即可。
想要添加一張新的表,則創建一個新的類,寫好字段及其對應get和set方法,然後將它添加到映射模型列表中,版本號+1即可。
5. 使用LitePal進行CRUD操作
LitePal進行表管理操作時不需要模型類有任何的繼承結構,但是進行CRUD操作時就不行,必須要繼承自DataSupport類才行。
A.) 添加數據
將Book類加上繼承結構:extends DataSupport
創建模型實例,再將所有要存儲的數據設置好,最後調用一下save();
Button addData = (Button)findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public 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("Unknow");
book.save();//繼承自DataSupport
}
});
B.) 更新數據
a.)最簡單的一種更新方式就是對已存儲的對象重新設值,然後重新調用save()即可。
用 model.isSaved() 方法判斷對象是否已經存儲。true 已存儲 false 未存儲
返回true的兩種情況:
- 已經調用過model.save()方法去添加數據,此時的model會被認爲已經存儲。
- model對象是通過LitePal提供的查詢API查出來的,由於是從數據庫中查出來的對象,所有也認爲是已存儲的對象。
Button updateData = (Button)findViewById(R.id.update_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public 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("Unknow");
book.save();//繼承自DataSupport
book.setPrice(10.99);
book.save();
}
});
b.)使用updateAll()方法
Button updateData = (Button)findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Book book = new Book(); //new 出一個Book實例
//設置要更新的數據
book.setPrice(14.95);
book.setPress("Anchor");
//調用updateAll()方法去執行更新操作
//該方法可以制定一個約束條件,若不指定,則表示更新所有數據
book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");
}
});
注意:在使用updateAll()時,當要把一個字段的值更新爲默認值時是不可以使用上面的方式來set數據的,而是使用setToDefault(列名)方法
C.) 刪除數據
a.)方法一:直接調用已存儲對象的 delete() 方法。
b.)方法二:調用 DataSupport.deleteAll(…) 方法。
參數一:指定刪除哪張表的數據。
剩下的參數:用於指定約束條件,和給佔位符賦值。
如若不指定約束條件,則表示刪除表中所有數據。
Button deleteData = (Button)findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DataSupport.deleteAll(Book.class,"price < ?","15");
}
});
D.) 查詢數據
List<Book> books = DataSupport.findAll(Book.class);//參數指定要查詢的表
查詢API:
- Book firstBook = DataSupport.findFirst(Book.class);//獲取第一條數據
- Book lastBook = DataSupport.findLast(Book.class);//獲取最後一條數據
- 可通過連綴查詢定製更多查詢功能:
- select()指定查詢哪幾列數據,對應SQL中的SELECT關鍵字
- where()指定查詢的約束條件,對應SQL中的WHERE關鍵字
- order()指定結果的排序方式 desc降序 asc(默認值)升序,對應SQL中的ORDER BY關鍵字
- limit(n)指定查詢結果的數量爲n條數據。
- limit(n).offset(m)指定查詢結果的偏移量,查詢表中第m+1,m+2,…,m+n條數據。
- limit()和offset()方法共同對應了SQL中的LIMIT關鍵字
//查詢Book表中第11-20條
//滿足頁數大於400這個條件的name,author和pages這3列數據,
//並將查詢結果按照頁數升序排列。
List<Book> books = DataSupport.select("name","author","pages")
.where("pages > ?","400")
.order("pages")
.limit(10)
.offset(10)
.find(Book.class);
E.) LitePal也支持原生的SQL查詢,findBySQL(SQL語句,若干參數指定佔位符的值),返回的是Cursor對象。
整理學習自郭霖大佬的《第一行代碼》
目前小白一名,持續學習Android中,如有錯誤請批評指正!