Android 學習之《第一行代碼》第二版 筆記(十四)詳解持久化技術(二)

持久化技術之SQLite 數據庫存儲

一、SQLite數據庫

SQLite是一款輕量級的關係型數據庫,運算速度非常快,佔用資源少。支持標準的SQL語法,遵循數據庫的ACID事務。不用設置用戶名和密碼即可使用。

二、SQLiteOpenHelper幫助類

  1. 這是一個抽象類,使用需要創建一個類繼承它,並重寫兩個抽象方法(onCreate(…) && onUpgrade(…)),在這兩個方法中去實現創建、升級數據庫的邏輯。
  2. 兩個重要方法:getReadableDatabase()getWritableDatabase()
    這兩個方法都可以創建或打開一個現有的數據庫,並返回一個可對數據庫進行讀寫操作的對象。
  3. 兩個構造函數,一般使用參數少的那個。
    public 類名 (…) 四個參數:
    參數一:Context;
    參數二:(String)數據庫名;
    參數三:允許查詢數據的時候返回一個自定義的Cursor,一般傳入null;
    參數四:當前數據庫的版本號。
  4. 數據庫文件會存放在/data/data//databases/目錄下
  5. 使用 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. 效果界面:

數據庫1
數據庫2

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中,如有錯誤請批評指正!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章