1. 程式人生 > >在 Android 應用中使用資料庫

在 Android 應用中使用資料庫

概述

在 Android 程式碼中建立和修改 SQLite 資料庫,我們可以參考 Android 文件 Save data using SQLite,我們在 Android 中需要採取兩個基本步驟來設定 SQLite 就可以和資料庫互動了。如下所示:

  1. Define a schema and contract-建立架構和契約類
  2. Create a database using an SQLOpenHelper-使用 SQLiteOpenHelper 建立資料庫

完成上面兩步後就可以執行建立、讀取、更新和刪除我們的資料的操作了。

建立架構和契約類

建立資料庫架構其實就是需要規劃資料庫結構

。所以我們需要問兩個問題:

  1. 表格的名稱是什麼?
  2. 表格裡列的名稱和資料型別是什麼?

還記得下面的語句嗎?

CREATE TABLE <table_name> (<column_name_1> <data_type_1>, <column_name_2> <data_type_2>, ...);

以寵物收容所場景為例:

Attribute Storage Class
Name(寵物名) TEXT(文字)
Breed(品種) TEXT(文字)
Gender(公母) INTEGER(0-未知,1-公、2-母)
Weight(體重) INTEGER(為了簡單)

繪製表格,規劃資料庫結構
表格

建立 Contract (契約)類

為什麼要使用 Contract 類?

看個建立表格的兩個例子:

String makeTableStatement = "CREATE TABLE entry (_id INTEGER PRIMARY KEY, entryid TEXT, title TEXT, subtitle TEXT)";

---

String SQL_CREATE_ENTRIES = "CREATE TABLE" + FeedEntry.TABLE_NAME + "("
+ FeedEntry._ID + "INTEGER PRIMARY KEY," + FeedEntry.COLUMN_NAME_ENTRY_ID + "TEXT," + FeedEntry.COLUMN_NAME_TITLE+"TEXT);"

觀察上述程式碼,我們發現第二個建立表格的語句中 表格名稱和列名稱儲存在了常量裡。 在生成 SQL 指令時,消除了出現拼寫錯誤的可能性,或者不小心將不應該大寫的字母大寫了。如果想更改實際的列名稱,也只需在一個地方更改下就行了。

總結使用 Contract 類的三個理由:

  • 幫助我們定義架構,規定了去哪裡查詢資料庫常量;
  • 在生成 SQL 指令時,可以幫助我們避免拼寫錯誤;
  • 使我們更容易更新資料庫架構

建立 Contract 類

根據之前規劃的資料庫結構即我們定義的架構,我們在專案的主包下面建立一個包名為 data,然後在改包下建立一個新的 java 類名為 PetContract,將該類用 final 修飾,因為它只是用來提供常量,我們不需要擴充套件或為此外部類實現任何內容。使用我們定義的架構,為每個表格建立一個內部類,併為每個列標題建立常量,程式碼如下所示:

public final class PetContract {
    private PetContract() {}

    public static final class PetEntry implements BaseColumns {

        public final static String TABLE_NAME = "pets";

        public final static String _ID = BaseColumn._ID;
        public final static String COLUMN_PET_NAME = "name";
        public final static String COLUMN_PET_BREED = "breed";
        public final static String COLUMN_PET_GENDER = "gender";
        public final static String COLUMN_PET_WEIGHT = "weight";

        public final static int GENDER_UNKONWN = 0;
        public final static int GENDER_MALE = 1;
        public final static int GENDER_FORMALE = 2;
    }
}

使用 SQLiteOpenHelper 建立資料庫

dada 包中建立一個新的 PetDbHelper 類,該類繼承自 SQLiteOpenHelper類。我們需要重寫 onCreate()onUpGrade() 方法,並且為資料庫名稱和版本建立常量,同時別忘了建立該類的建構函式,還要為用來建立表格的 SQLite 指令建立一個字串常量。

/**
 * Database helper for Pets app. Manages database creation and version management.
 */
public class PetDbHelper extends SQLiteOpenHelper {

    public static final String LOG_TAG = PetDbHelper.class.getSimpleName();

    /** Name of the database file */
    private static final String DATABASE_NAME = "shelter.db";

    /**
     * Database version. If you change the database schema, you must increment the database version.
     */
    private static final int DATABASE_VERSION = 1;

    /**
     * Constructs a new instance of {@link PetDbHelper}.
     *
     * @param context of the app
     */
    public PetDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * This is called when the database is created for the first time.
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        // Create a String that contains the SQL statement to create the pets table
        String SQL_CREATE_PETS_TABLE =  "CREATE TABLE " + PetEntry.TABLE_NAME + " ("
                + PetEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                + PetEntry.COLUMN_PET_NAME + " TEXT NOT NULL, "
                + PetEntry.COLUMN_PET_BREED + " TEXT, "
                + PetEntry.COLUMN_PET_GENDER + " INTEGER NOT NULL, "
                + PetEntry.COLUMN_PET_WEIGHT + " INTEGER NOT NULL DEFAULT 0);";

        // Execute the SQL statement
        db.execSQL(SQL_CREATE_PETS_TABLE);
    }

    /**
     * This is called when the database needs to be upgraded.
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // The database is still at version 1, so there's nothing to do be done here.
    }
}

寫了這麼多我們也不知道資料庫是否能正常執行,我們可以通過下面的方法進行檢查。

檢視資料庫是否正常運轉:

  • 建立 displayDatabaseInfo() 方法,將其放置到我們程式碼中,只有 SQL 能正常運轉時,該方法才能運轉。
  • 將資料庫下載到本地,通過在終端使用 SQL 語句檢視。
 @Override
    protected void onCreate(Bundle savedInstanceState) {
    ...
    displayDatabaseInfo()
    }

/**
     * Temporary helper method to display information in the onscreen TextView about the state of
     * the pets database.
     */
    private void displayDatabaseInfo() {
        // To access our database, we instantiate our subclass of SQLiteOpenHelper
        // and pass the context, which is the current activity.
        PetDbHelper mDbHelper = new PetDbHelper(this);

        // Create and/or open a database to read from it
        SQLiteDatabase db = mDbHelper.getReadableDatabase();

        // Perform this raw SQL query "SELECT * FROM pets"
        // to get a Cursor that contains all rows from the pets table.
        Cursor cursor = db.rawQuery("SELECT * FROM " + PetEntry.TABLE_NAME, null);
        try {
            // Display the number of rows in the Cursor (which reflects the number of rows in the
            // pets table in the database).
            TextView displayView = (TextView) findViewById(R.id.text_view_pet);
            displayView.setText("Number of rows in pets database table: " + cursor.getCount());
        } finally {
            // Always close the cursor when you're done reading from it. This releases all its
            // resources and makes it invalid.
            cursor.close();
        }
    }

連線資料庫流程

這裡寫圖片描述

PetDbHelper mDbHelper = new PetDbHelper(this);
SQLiteDatabase db = mDbHelper.getReadableDatabase();

請求資料庫,mDbHelper 將檢查是否已經存在一個數據庫?

如果不存在,則 PetDbHelper 的例項將使用 onCreate() 方法建立一個數據庫,接著建立 SQLiteDatabase 的例項物件返回給 Activity。

如果資料庫已經存在,PetDbHelper 的例項將不會呼叫 onCreate() 方法,相反,將建立一個 SQLiteDatabase 的例項物件並關聯現有的資料庫,然後將該物件返回給請求資料庫的 Activity。

最終時幫助我們建立 SQLiteDatabase 物件與 shelter 資料庫關聯的 SQLiteDatabase 物件,之後我們就可以通過該物件向 shelter 資料庫傳達 SQLite 指令了。

從裝置中提取資料庫

瞭解了資料庫的連線流程,我們就可以檢測資料庫是否正常運轉。當然我們還可以通過另一種方式,通過檢視裝置的檔案系統,使我們能夠實際地看到 petDbHelper 類在建立資料庫,然後當資料庫實際建立好後看到 .db 檔案出現。

這裡寫圖片描述

在這之前我們可以開啟手機應用資訊,將 pets 應用中清楚快取和儲存資料。同時註釋掉程式碼中的 displayDatabaseInfo() 方法。在 Android Studio 中點選 Android Device Monitor 按鈕,選擇我們當前使用的模擬器,轉到 File Explorer,該功能可以讓我們瀏覽裝置的檔案系統,點開 data 資料夾,再點開裡面的 data 資料夾,在裡面找到我們應用的包,再開啟包,查詢檔案,我們會發現沒有 .db 檔案,這時我們的 getReadableDatabase() 方法尚未被呼叫。

這裡寫圖片描述

當我們把剛才註釋掉的 displayDatabaseInfo() 方法重新恢復,執行程式碼後,我們再檢視檔案系統中該包下有了 shelter.db 資料庫檔案了。再執行一次就不會再出現新的檔案了。如果想看 shelter.db 檔案中的內容,可以通過 DDMS 下載檔案,儲存到電腦本地上,通過 SQL 語言在終端檢視。

這裡寫圖片描述

資料庫物件及插入資料

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);

// Insert the new row, returning the primary key value of the new row
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);

ContentValues 中儲存了大量的鍵值對,鍵是資料庫中的列名稱,值就是插入的值。舉例:

這裡寫圖片描述

ContentValues values = new ContentValues();
values.put(PetEntry.COLUMN_PET_NAME, "Garfield");
values.put(PetEntry.COLUMN_PET_BREED, "Tabby");
values.put(PetEntry.COLUMN_PET_GENDER, PetEntry.GENDER_MALE);
values.put(PetEntry.COLUMN_PET_WEIGHT, 7);
db.insert(PetEntry.TABLE_NAME, null, values);

注意 insert() 方法會返回新插入的 ID,如果出現錯誤它會返回 -1。

資料庫查詢方法

我們可以通過 db.rawquery() 方法來讀取資料庫,但不建議使用。就像存在 SQLiteDatabase.insert() 方法一樣,我們可以使用 SQLiteDatabase.query() 方法來讀取資料庫,它會幫助我們構建查詢,從而避免語法錯誤。我們看下 Android 文件來了解下:

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    BaseColumns._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_SUBTITLE
    };

// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };

// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";

Cursor cursor = db.query(
    FeedEntry.TABLE_NAME,   // The table to query
    projection,             // The array of columns to return (pass null to get all)
    selection,              // The columns for the WHERE clause
    selectionArgs,          // The values for the WHERE clause
    null,                   // don't group the rows
    null,                   // don't filter by row groups
    sortOrder               // The sort order
    );

觀察上述程式碼,我們發現首先定義了一個字串陣列 projection,它其實是指我們想要獲取的列名稱,就像這樣的語句:

SELECT name, breed FROM pets;

定義 projection 使我們能夠指定想要獲取的具體列,預設則獲取所有列,類似於:

SELECT * FROM pets;

projection 大小會影響到效能

呼叫 query() 方法,該方法具有大量輸入引數,代表了 SELECT 語句的不同部分。第一個引數 projection 我們已經在上面介紹了,接下來 selectionselectionArgs 處理的是可選 WHERE 條件。例如根據 ID 選擇單個寵物:

SELECT * FROM pets WHERE _id = 1;

使用 selectionselectionArgs 屬性就是這樣的:

// Define 'where' part of query
String selection = PetEntry._ID + "?";
// Specify arguments in placeholder order
String[] selectionArgs = {"1"}; 

selection 引數是 WHERE 關鍵字之後的型別為 String 的,這裡使用 ? 作為佔位符,然後填充為 selectionArgs 引數中的值,它是一個字串陣列,負責替換這裡 selection 中的問號,這裡將其設為 1。

為什麼使用 ? 號和 selectionArgs ,而不是直接寫成 1 ?

這裡沒有區別,我們可以將 selectionArgs 設為 null,將 ? 號改為 1 。但是在某些情況下,選擇內容可能來自表格,使用佔位符是一種安全措施,可以防止出現 SQL 注入攻擊,也就是使用者不按套路出牌,編輯一些程式碼類內容輸入,使我們的查詢語句出現歧義錯誤。

呼叫 query() 方法後會返回一個 Cursor 物件,它是一種可以捕獲資料庫中所有子集的物件。

什麼是 Cursor

簡而言之,Cursor 是指 代表資料庫中多行內容的物件。假設如下是我們整個資料庫的架構:
這裡寫圖片描述

如果我們針對這個資料庫中這個表格呼叫 query() 方法,我們可以指定希望返回的值,然後這些資訊就會以 Cursor 物件的形式返回給我們。

如果我們的選擇引數是

SELECT * FROM pets

我們獲得的 Cursor 物件,其中包含資料庫中的各行內容。

如果我們希望進一步指定僅選擇 pets 表格中寵物名為 Toto 的行,我們獲得的 CUrsor 物件就是其中所有行裡的寵物名是 Toto 的行,結果如下:
這裡寫圖片描述

我們再來看另一個示例,假設從 pets 表格中選擇 name 和 breed,也就是將白鴿縮小為僅返回這兩列:
這裡寫圖片描述

也就是將表格縮小為僅返回這兩列,返回的 Cursor 物件包含所有行,但是僅包含 name 和 breed 列的詳情。

Cursor 包含了是我們能夠訪問和瀏覽 Cursor 物件的方法,如果 Cursor 包含多行的話就可以瀏覽各行。

Cursor.getCount()

Cursor 物件代表了資料庫中的行和列,此外,他還提供了這些行中的當前位置,為了能獲得特定的資料,我們需要將 Cursor 移動到我們所需要的精確行

這裡寫圖片描述

當我們第一次獲取 Cursor 時,位置從 -1 開始,這是無效的位置,首個可用位置為0,然後逐步遞增。

Cursor.moveToFirst

該方法既將 Cursor 中的位置移動到結果中的第一行,這使我們能夠訪問第一條記錄裡的資料。

這裡寫圖片描述

Cursor.moveToLast

該方法會使我們跳到這裡的最後一行。

這裡寫圖片描述

Cursor.moveToPosition(int position)

該方法會將 Cursor 位置移動到指定位置。

這裡寫圖片描述

上面方法返回的都是 Boolean 型別的值,這樣可以幫助我們判斷實際是否移動到了該位置。比如我們當前 Cursor 已經移動到了最後一行,再呼叫向下移動的方法就會返回 false。

我們可以使用不同的 get 方法從資料庫中獲取特定的值。
這裡寫圖片描述

使用 getColumnIndex(String columnName)方法,來根據名稱來獲取列的索引。舉例:

這裡寫圖片描述

對於 Cursor 要注意的一個事項是,使用完畢後,一定要記得呼叫 cursor.close(),這樣會完全清空 Cursor 使其無效。僅在完全操作完畢後呼叫該方法。不關閉 Cursor 的話,會因記憶體洩漏而降低效能。

文章只是作為個人記錄學習使用,如有不妥之處請指正,謝謝。

相關推薦

Android 應用使用資料庫

概述 在 Android 程式碼中建立和修改 SQLite 資料庫,我們可以參考 Android 文件 Save data using SQLite,我們在 Android 中需要採取兩個基本步驟來設定 SQLite 就可以和資料庫互動了。如下所示: De

unity3d開發的android應用增加AD系統的詳細步驟

查看 發的 b- sset @override 大小 代碼 nac cal unity3d開發的android應用中增加AD系統的詳細步驟 博客分類: Unity3d unity3d Unity3d已經支持android,怎樣在程序裏增加admob?

Android應用使用哪個WebSocket套件?

我Google 了一下,找到很多個套件可以使用。 目前這套件在 2017-10-27 的星星數目: 4673 目前這套件在 2017-10-27 的星星數目: 698 目前這套件在 2017-10-27 的星星數目: 3317 上面 3個,照星星數來排,應該用 4000星的,我試了一下,範例沒辦

Android應用TextView跑馬燈效果

往往有很多時候我們所使用的TextView能顯示的內容字數是有限的,有時當我們要設定的內容長度過長時,我們的UI頁面就會出現一些不和諧的現象。 處理方法(很少有人把...放在前面的吧…^ v ^): 1,尾部(...)處理      android:ellipsize=

android應用為按鍵新增聲音

    soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);     soundPoolMap = new HashMap<Integer, Integer>();       soundPoolMap.put(1, soun

Android應用使用百度地圖API並新增標註(一)

網上一些資料這種的內容已經過時了,這裡是最新的內容,如果哪裡不對,請吐槽。。。 1)下載百度地圖移動版API(Android)開發包       要在Android應用中使用百度地圖API,就需要在工程中引用百度地圖API開發包,這個開發包包含兩個檔案: 2)申請A

解決mysql和視覺化工具Navicat與web應用資料庫傳輸中文亂碼問題

  關於網上解決亂碼的問題有許多也很成熟了,將mysql和他的視覺化工具Navicat之間統一編碼問題也有許多,本篇文章旨在回顧mysql與Navicat之間的統一編碼的所有方式,和web應用中傳輸中文亂碼做統一規劃,由於本人水有限,文中有不少錯誤的地方望批評改正。   

喚醒鎖: 檢測 Android* 應用的 No-Sleep(無法進入睡眠)問題

如果 Android* 應用使用喚醒鎖不當,將會顯著增加電池耗電量。 在本文中,我們將介紹一些提示和技巧,幫助您瞭解如何確認與誤用喚醒鎖有關的 No Sleep 漏洞。 1. 介紹 限制電池耗電量對智慧手機非常有必要。 為了獲得最大的自主性,Android 的作業系統設計可在檢測到系統上無使用者活動時

Android應用使用自定義證書的HTTPS連線(下)

因為這部分才是本文的重點,要說得詳細一點,所以單獨做成一篇來說。安全地使用自定義證書的HTTPS連線方式終極解決方案是:把證書編譯到應用中去,由應用自己來驗證證書。生成KeyStore要驗證自定義證書,首先要把證書編譯到應用中去,這需要JSSE提供的keytool工具來生成K

Android應用使用百度地圖API定位自己的位置(二)

百度地圖SDK為開發者們提供瞭如下型別的地圖覆蓋物: 我的位置圖層(MyLocationOverlay):用於顯示使用者當前位置的圖層(支援自定義位置圖示); Poi搜尋結果圖層(PoiOverlay):用於顯示興趣點搜尋結果的圖層; 路線圖層(RouteOve

Android應用訪問HTTPS方式

HTTPS和HTTP的區別 一、https協議需要到ca申請證書,一般免費證書很少,需要交費。 二、http是超文字傳輸協議,資訊是明文傳輸,https 則是具有安全性的ssl加密傳輸協議。 三、http和https使用的是完全不同的連線方式,用的埠也不一樣,前者是80,後者是443。 四、htt

Android應用開啟百度地圖、高德地圖、網頁版百度地圖

1.需求 在Android應用中開啟百度地圖或者高德地圖進行路線規劃,如果沒有安裝則開啟網頁百度地圖進行路線規劃。 2.API 2.1 開啟百度地圖應用 開啟文件可以看到功能還是很多的,這裡只介紹 公交、駕車、導航、步行和騎行導航 注:

Android開發學習之路--在Android應用愉快地寫C/C++程式碼

1 前言 一直想在android層面寫c程序,然後java可以與c程序互動,以前在android原始碼中想玩就可以直接在init.rc中加上交叉編譯好的c程序就可以了,而在ide中,也就是ndk編譯後各種許可權問題就有點不得而知了。花了幾天時間研究實踐,也終於

Android應用去掉標題欄方法總結(Eclipse+Android Studio)

Eclipse 1.在程式程式碼中實現 需要注意的是: this.requestWindowFeature(Window.FEATURE_NO_TITLE);

Android開發資料庫(sqlite)的檢視及一些問題的解決方法

最近在學習Android中的Sqlite遇到一些問題,這裡做一下總結。 一、建立資料庫 首先你要新建一個使用Sqlite的APP,開發工具Ecplise、AndroidStudio都可,這裡以後者AS為例。如果你沒有現成的程式,可以用博主的這個、 檔名

android應用不響應按鍵事件(俗稱攔截按鍵)

在應用中重寫 dispatchKeyEvent函式:例項中為遮蔽KEYCODE_ENTER事件 public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode();if(K

資料庫連線池應用資料庫伺服器斷開超時連線的問題

資料庫應用開發過程中,我們可能會遇到一個問題:應用使用了資料庫連線池,每經過指定時間後,發出到資料庫伺服器的任何請求都會失敗,而且有且僅有一次失敗,之後的正常訪問都沒有問題。尤其是在Web應用中,如果晚上時段沒有訪問,而第二天第一個訪客的經歷就是碰到一個數據庫訪問錯誤,

Android應用使用自定義證書的HTTPS連線(上)

對於初次接觸https有一定的幫助,本文屬於轉載篇。 原文地址:http://blog.csdn.net/raptor/article/details/18896375 前言 由於移動裝置使用的網路環境各種各樣,而且常常接入不安全的公共WIFI——如果你對公共WIFI

android專案資料庫獲取的方法

android資料庫可以存放到任意位置,然後到相應的位置讀取資料庫。經常有一些資料在軟體釋出之前就已經存在,所以獲取資料庫相當重要。獲取的方法有兩種: 二是把資料庫放入打包檔案中,啟動程式複製資料庫到相應的位置。 下面詳細介紹一些第二種方法,把資料庫檔案放入資原始檔中,然後在軟體釋出的時候,複製到相應的

Android學習探索之Java 8 在Android 開發應用

相關 概念 容易 並不是 min etc bstr trac flavor 前言: Java 8推出已經將近2年多了,引入很多革命性變化,加入了函數式編程的特征,使基於行為的編程成為可能,同時減化了各種設計模式的實現方式,是Java有史以來最重要的更新。但是Androi