android資料庫的簡單Demo(原生版+Google版)

weixin_34138377發表於2015-11-13

本文出自 “阿敏其人” 簡書部落格,轉載或引用請註明出處。

android和ios的資料庫都是用SQLite來實現。
在安卓裡面資料庫怎麼用呢,簡單來說可用分為以下三步:
1、新建一個資料庫幫助類,繼承自SQLiteOpenHelper,複寫onCreate() 和 onUpgrade()
2、新建一個資料裡操作類(dao類),利用 資料庫幫助類 得到資料庫的例項,然後在dao類裡面編寫 增刪改查 的方法
3、在Activity裡面例項化資料庫操作類(dao類),呼叫對應的 增刪查改方法。
(對資料進行的操作也就無非四種,即CRUD。其中C代表新增(Create),R代表查詢(Retrieve),U代表更新(Update),D代表刪除(Delete)。有人叫增刪改查,有人叫增刪查改)

來圖片最直接

1083096-4b6cb921f2ead682.gif
增刪改查演示.gif

結構簡圖

1083096-6300023bb1e3ded0.png
資料庫demo結構簡圖.png


一、SQLite資料庫簡介

  • 輕量級 : SQLite資料庫是一個輕量級的資料庫, 適用於少量資料的CURD;
  • 檔案本質 : SQLite資料庫支援大部分SQL語法, 允許使用SQL語句運算元據庫, 其本質是一個檔案, 不需要安裝啟動;
  • 資料讀寫 : SQLite資料庫開啟只是開啟了一個檔案的讀寫流, 如果有大資料量讀寫, 需要高併發儲存, 那麼就不應該使用SQLite;

二、安卓資料如何使用

1、編寫資料庫幫助類

  • 新建一個資料庫幫助類,繼承自SQLiteOpenHelper
  • 編寫建構函式
  • 複寫onCreate() 和 onUpgrade()

程式碼如下:

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * 資料庫Helper類,必須繼承自 SQLiteOpenHelper
 * 當一個繼承自 SQLiteOpenHelper 後需要複寫兩個方法,分別是 onCreate()  和 onUpgrade()
 * onCreate(): onCreate是在資料庫建立的時候呼叫的,主要用來初始化資料表結構和插入資料初始化的記錄
 * onUpgrade():onUpGrade是在資料庫版本升級的時候呼叫的,主要用來改變表結構
 *
 *
 *  資料庫幫助類要做的事情特別簡單:
 *  1、複寫onCreate()  和 onUpgrade()方法
 *  2、在這兩個方法裡面填寫相關的sql語句
 *
 *
 */
public class MyDBHelper extends SQLiteOpenHelper{

    public MyDBHelper(Context context) {
        /**
         * 引數說明:
         *
         * 第一個引數: 上下文
         * 第二個引數:資料庫的名稱
         * 第三個引數:null代表的是預設的遊標工廠
         * 第四個引數:是資料庫的版本號  資料庫只能升級,不能降級,版本號只能變大不能變小
         */
        super(context, "mintest.db", null, 2);
    }


    /**
     * onCreate是在資料庫建立的時候呼叫的,主要用來初始化資料表結構和插入資料初始化的記錄
     *
     * 當資料庫第一次被建立的時候呼叫的方法,適合在這個方法裡面把資料庫的表結構定義出來.
     * 所以只有程式第一次執行的時候才會執行
     * 如果想再看到這個函式執行,必須寫在程式然後重新安裝這個app
     */

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table contactinfo (id integer primary key autoincrement, name varchar(20), phone varchar(20))");
    }


    /**
     * 當資料庫更新的時候呼叫的方法
     * 這個要顯示出來得在上面的super語句裡面版本號發生改變時才會 列印  (super(context, "itheima.db", null, 2); )
     * 注意,資料庫的版本號只可以變大,不能變小,假設我們當前寫的版本號是3,執行,然後又改成1,執行則報錯。不能變小
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("alter table contactinfo add account varchar(20)");
    }
}


2、資料庫操作類(dao類)

dao類在這裡做得事情特別簡單:

  • 1、定義一個構造方法,利用這個方法去例項化一個 資料庫幫助類
  • 2、編寫dao類的對應的 增刪改查 方法。

程式碼如下:


package amqr.com.dbanddao.dao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import amqr.com.dbanddao.db.MyDBHelper;

/**
 *  ContactInjfoDao  資料庫操作類  dao字尾的都是資料庫操作類
 *
 *  我們這裡的每一個 增刪改查 的方法都通過getWritableDatabase()去例項化了一個資料庫,這裡必須這麼做
 *  不客氣抽取 成為一個成員變數, 否則報錯,若是覺得麻煩可以通過定義方法來置為null和重新賦值
 *
 *  —— 其實dao類在這裡做得事情特別簡單:
 *  1、定義一個構造方法,利用這個方法去例項化一個  資料庫幫助類
 *  2、編寫dao類的對應的 增刪改查 方法。
 *
 */
public class ContactInjfoDao {

    private MyDBHelper mMyDBHelper;

    /**
     * dao類需要例項化資料庫Help類,只有得到幫助類的物件我們才可以例項化 SQLiteDatabase
     * @param context
     */
    public ContactInjfoDao(Context context) {
        mMyDBHelper=new MyDBHelper(context);
    }

    // 將資料庫開啟幫幫助類例項化,然後利用這個物件
    // 呼叫谷歌的api去進行增刪改查

    // 增加的方法嗎,返回的的是一個long值
    public long addDate(String name,String phone){
        // 增刪改查每一個方法都要得到資料庫,然後操作完成後一定要關閉
        // getWritableDatabase(); 執行後資料庫檔案才會生成
        // 資料庫檔案利用DDMS可以檢視,在 data/data/包名/databases 目錄下即可檢視
        SQLiteDatabase sqLiteDatabase =  mMyDBHelper.getWritableDatabase();
        ContentValues contentValues=new ContentValues();

        contentValues.put("name",name);
        contentValues.put("phone", phone);
        // 返回,顯示資料新增在第幾行
        // 加了現在連續新增了3行資料,突然刪掉第三行,然後再新增一條資料返回的是4不是3
        // 因為自增長
        long rowid=sqLiteDatabase.insert("contactinfo",null,contentValues);

        sqLiteDatabase.close();
        return rowid;
    }


    // 刪除的方法,返回值是int
    public int deleteDate(String name){
        SQLiteDatabase sqLiteDatabase = mMyDBHelper.getWritableDatabase();
        int deleteResult = sqLiteDatabase.delete("contactinfo", "name=?", new String[]{name});
        sqLiteDatabase.close();
        return deleteResult;
    }

    /**
     * 修改的方法
     * @param name
     * @param newPhone
     * @return
     */
    public int updateData(String name,String newPhone){
        SQLiteDatabase sqLiteDatabase = mMyDBHelper.getWritableDatabase();
        ContentValues contentValues =new ContentValues();
        contentValues.put("phone", newPhone);
        int updateResult = sqLiteDatabase.update("contactinfo", contentValues, "name=?", new String[]{name});
        sqLiteDatabase.close();
        return updateResult;
    }

    /**
     * 查詢的方法(查詢電話)
     * @param name
     * @return
     */
    public String alterDate(String name){
        String phone = null;

        SQLiteDatabase readableDatabase = mMyDBHelper.getReadableDatabase();
        // 查詢比較特別,涉及到 cursor
        Cursor cursor = readableDatabase.query("contactinfo", new String[]{"phone"}, "name=?", new String[]{name}, null, null, null);
        if(cursor.moveToNext()){
            phone=cursor.getString(0);
        }
        cursor.close(); // 記得關閉 corsor
        readableDatabase.close(); // 關閉資料庫
        return phone;
    }


}


3、在Activity裡面利用dao類運算元據庫

xml佈局檔案

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity" >
    <EditText
        android:id="@+id/mEtName"
        android:hint="請輸入聯絡人的姓名"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
    </EditText>
    <EditText
        android:id="@+id/mEtPhone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入聯絡人的電話"
        android:inputType="phone" />
    <Button
        android:onClick="add"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="新增" />
    <Button
        android:onClick="delete"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="刪除" />
    <Button
        android:onClick="update"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="修改" />
    <Button
        android:onClick="query"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="查詢" />
</LinearLayout>


4、Activity程式碼:

package amqr.com.dbanddao;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import amqr.com.dbanddao.dao.ContactInjfoDao;

public class MainActivity extends AppCompatActivity {
   private EditText mEtName;
   private EditText mEtPhone;
   private ContactInjfoDao mDao;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       mDao=new ContactInjfoDao(MainActivity.this);
       mEtName= (EditText) findViewById(R.id.mEtName);
       mEtPhone= (EditText) findViewById(R.id.mEtPhone);

   }

   public void add(View view){

       String name=mEtName.getText().toString().trim();
       String phone=mEtPhone.getText().toString().trim();
       if(TextUtils.isEmpty(name)||TextUtils.isEmpty(phone)){
           Toast.makeText(this,"填寫不完整",Toast.LENGTH_SHORT).show();
           return;
       }else{
           long addLong = mDao.addDate(name, phone);
           if(addLong==-1){
               Toast.makeText(this,"新增失敗",Toast.LENGTH_SHORT).show();
           }else{
               Toast.makeText(this,"資料新增在第  "+addLong+"   行",Toast.LENGTH_SHORT).show();
           }

       }
   }

   public void delete(View view){
       String name=mEtName.getText().toString().trim();


       if(TextUtils.isEmpty(name)){
           Toast.makeText(this,"填寫不完整",Toast.LENGTH_SHORT).show();
           return;
       }else{
           int deleteDate = mDao.deleteDate(name);
           if(deleteDate==-1){
               Toast.makeText(this,"刪除失敗",Toast.LENGTH_SHORT).show();
           }else{
               Toast.makeText(this,"成功刪除  "+deleteDate+"   條資料",Toast.LENGTH_SHORT).show();
           }

       }

   }


   public void update(View view){

       String name=mEtName.getText().toString().trim();
       String phone=mEtPhone.getText().toString().trim();
       if(TextUtils.isEmpty(name)||TextUtils.isEmpty(phone)){
           Toast.makeText(this,"填寫不完整",Toast.LENGTH_SHORT).show();
           return;
       }else{
           int count=mDao.updateData(name, phone);
           if(count==-1){
               Toast.makeText(this,"更新失敗",Toast.LENGTH_SHORT).show();
           }else{
               Toast.makeText(this,"資料更新了  "+count+"   行",Toast.LENGTH_SHORT).show();
           }

       }
   }


   public void query(View view){

       String name=mEtName.getText().toString().trim();
       
       if(TextUtils.isEmpty(name)){
           Toast.makeText(this,"填寫不完整",Toast.LENGTH_SHORT).show();
           return;
       }else{
           String phoneResult = mDao.alterDate(name);

           Toast.makeText(this,"手機號碼為:    "+phoneResult,Toast.LENGTH_SHORT).show();


       }
   }

}

至此完成。

三、資料庫檔案在那裡

當我們呼叫dao裡面的方法,dao裡面的方法就都會例項化資料庫,比如
JAVA SQLiteDatabase sqLiteDatabase = mMyDBHelper.getWritableDatabase();
當這句程式碼的getWritableDatabase();執行後生成資料庫檔案

具體位置在data/data/包名/databases路徑下

1083096-5a5029beb5a03eeb.png
資料庫檔案.png

四、簡單的資料庫語句知識

在Android平臺上,整合了一個嵌入式關係型資料庫—SQLite,SQLite3支援 NULL、INTEGER、REAL(浮點數字)、TEXT(字串文字)和BLOB(二進位制物件)資料型別。
雖然它支援的型別只有五種,但實際上sqlite3也接受varchar(n)、char(n)、decimal(p,s) 等資料型別,只不過在運算或儲存時會轉成對應的五種資料型別。
SQLite最大的特點是你可以把各種型別的資料儲存到任何欄位中,而不用關心欄位宣告的資料型別是什麼。例如:可以在Integer型別的欄位中存放字串,或者在布林型欄位中存放浮點數,或者在字元型欄位中存放日期型值。 但有一種情況例外:定義為INTEGER PRIMARY KEY的欄位只能儲存64位整數, 當向這種欄位儲存除整數以外的資料時,將會產生錯誤。 另外,在編寫CREATE TABLE 語句時,你可以省略跟在欄位名稱後面的資料型別資訊,如下面語句你可以省略 name欄位的型別資訊:

CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))

.

SQLite可以解析大部分標準SQL語句,如:

  • 查詢語句:select * from 表名 where 條件子句 group by 分組字句 having ... order by 排序子句

如: select * from person
select * from person order by id desc
select name from person group by name having count(*)>1

分頁SQL與mysql類似,下面SQL語句獲取5條記錄,跳過前面3條記錄

select * from Account limit 5 offset 3 或者 select * from Account limit 3,5

  • 插入語句:insert into 表名(欄位列表) values(值列表)。如: insert into person(name, age) values(‘張三’,3)

  • 更新語句:update 表名 set 欄位名=值 where 條件子句。如:update person set name=‘張三‘ where id=10

  • 刪除語句:delete from 表名 where 條件子句。如:delete from person where id=10

獲取新增記錄後自增長的ID值:SELECT last_insert_rowid()


五、谷歌專版的增刪改查

在上面的程式碼中,我們的dao類的裡面的增刪改查的每一個方法都需要編寫你sql語句,sql語句比較麻煩,容易出錯,所以google封裝了一下sql語句,方便我們呼叫。

SQLiteDatabase中有幾個方法,可用於對資料庫進行增刪查改的操作。

1、SQLiteDatabase 增,insert()

  • 增 insert()
    insert()方法,這個方法就是專門用於新增資料的。

它接收三個引數,
第一個引數是表名,我們希望向哪張表裡新增資料,這裡就傳入該表的名字。
第二個引數用於在未指定新增資料的情況下給某些可為空的列自動賦值NULL,一般我們用不到這個功能,直接傳入null即可。第三個引數是一個ContentValues物件,它提供了一系列的put()方法過載,用於向ContentValues中新增資料,只需要將表中的每個列名以及相應的待新增資料傳入即可。

    /**
     * 新增一條記錄
     * @param name 聯絡人姓名
     * @param phone 聯絡人電話
     * @return 返回的是新增後在資料庫的行號  -1代表新增失敗
     */
    public long add(String name, String phone){
        SQLiteDatabase db = helper.getWritableDatabase();
        //db.execSQL("insert into contactinfo (name,phone) values (?,?)", new Object[]{name,phone});
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("phone", phone);
        //內部是組拼sql語句實現的.
        long rowid = db.insert("contactinfo", null, values);
        //記得釋放資料庫資源
        db.close();
        return rowid;
    }

2、SQLiteDatabase 刪,delete()

  • 刪 delete()

delete()方法專門用於刪除資料
這個方法接收三個引數,
第一個引數是表名,
第二、第三個引數又是用於去約束刪除某一行或某幾行的資料,不指定的話預設就是刪除所有行。


/**
     * 根據姓名刪除一條記錄
     * @param name 要刪除的聯絡人的姓名
     * @return 返回0代表的是沒有刪除任何的記錄 返回整數int值代表刪除了幾條資料
     */
    public int delete(String name){
        //判斷這個資料是否存在.
        SQLiteDatabase db = helper.getWritableDatabase();
        //db.execSQL("delete from contactinfo where name=?", new Object[]{name});
        int rowcount = db.delete("contactinfo", "name=?", new String[]{name});
        db.close();
        //再從資料庫裡面查詢一遍,看name是否還在
        return rowcount;
    }

2、SQLiteDatabase 改,update()

  • 改 update()方法用於對資料進行更新。
    這個方法接收四個引數,
    第一個引數是表名,在這裡指定去更新哪張表裡的資料。
    第二個引數是ContentValues物件,要把更新資料在這裡組裝進去。
    第三、第四個引數用於去約束更新某一行或某幾行中的資料,不指定的話預設就是更新所有行。

/**
     * 修改聯絡人電話號碼
     * @param newphone 新的電話號碼
     * @param name 要修改的聯絡人姓名
     * @return 0代表一行也沒有更新成功, >0 整數代表的是更新了多少行記錄
     */
    public int update(String newphone , String name){
        SQLiteDatabase db = helper.getWritableDatabase();
        //db.execSQL("update contactinfo set phone =? where name=?", new Object[]{newphone,name});
        ContentValues values = new ContentValues();
        values.put("phone", newphone);
        int rowcount =  db.update("contactinfo", values, "name=?", new String[]{name});
        db.close();
        return rowcount;
    }

4、SQLiteDatabase 查, query()

  • 查詢 query()

查詢是資料庫的重頭戲,SQL的全稱是Structured Query Language,翻譯成中文就是結構化查詢語言。它的大部功能都是體現在“查”這個字上的,而“增刪改”只是其中的一小部分功能。

SQLiteDatabase中還提供了一個query()方法用於對資料進行查詢。這個方法的引數非常複雜,最短的一個方法過載也需要傳入七個引數。

query()方法引數 對應SQL部分 描述
table from table_name 指定查詢的表名
columns select column1, column2 指定查詢的列名
selection where column = value 指定where的約束條件
selectionArgs - 為where中的佔位符提供具體的值
groupBy group by column 指定需要group by的列
having having column = value 對group by後的結果進一步約束
orderBy order by column1, column2 指定查詢結果的排序方式
/**
     * 查詢聯絡人的電話號碼
     * @param name 要查詢的聯絡人
     * @return 電話號碼
     */
    public String getPhoneNumber(String name){
        String phone = null;
        SQLiteDatabase db = helper.getReadableDatabase();
        //Cursor  cursor = db.rawQuery("select phone from contactinfo where name=?", new String[]{name});
        Cursor  cursor =  db.query("contactinfo", new String[]{"phone"}, "name=?", new String[]{name}, null, null, null);
        if(cursor.moveToNext()){//如果游標可以移動到下一位,代表就是查詢到了資料
            phone = cursor.getString(0);
        }
        cursor.close();//關閉掉遊標,釋放資源
        db.close();//關閉資料庫,釋放資源
        return phone;
    }

5 完整的程式碼示例

下面貼入一個使用google的SQLiteDatabase提供的insert等四個方法進行增刪查改的dao類(上面的佈局檔案,開啟幫助類不變)的完整程式碼:

package com.amqr.test.googledbdao.dao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.amqr.test.googledbdao.db.MyDBHelper;


/**
* 聯絡人資料庫表的訪問類
*/
public class ContactInfoDao {
   /**
    * 資料庫開啟的幫助類
    */
   private MyDBHelper helper;

   /**
    * 在構造方法裡面完成 必須要用的類的初始化
    * @param context
    */
   public ContactInfoDao(Context context) {
       helper = new MyDBHelper(context);
   }

   /**
    * 新增一條記錄
    * @param name 聯絡人姓名
    * @param phone 聯絡人電話
    * @return 返回的是新增後在資料庫的行號  -1代表新增失敗
    */
   public long add(String name, String phone){
       SQLiteDatabase db = helper.getWritableDatabase();
       //db.execSQL("insert into contactinfo (name,phone) values (?,?)", new Object[]{name,phone});
       ContentValues values = new ContentValues();
       values.put("name", name);
       values.put("phone", phone);
       //內部是組拼sql語句實現的.
       long rowid = db.insert("contactinfo", null, values);
       //記得釋放資料庫資源
       db.close();
       return rowid;
   }
   /**
    * 根據姓名刪除一條記錄
    * @param name 要刪除的聯絡人的姓名
    * @return 返回0代表的是沒有刪除任何的記錄 返回整數int值代表刪除了幾條資料
    */
   public int delete(String name){
       //判斷這個資料是否存在.
       SQLiteDatabase db = helper.getWritableDatabase();
       //db.execSQL("delete from contactinfo where name=?", new Object[]{name});
       int rowcount = db.delete("contactinfo", "name=?", new String[]{name});
       db.close();
       //再從資料庫裡面查詢一遍,看name是否還在
       return rowcount;
   }
   /**
    * 修改聯絡人電話號碼
    * @param newphone 新的電話號碼
    * @param name 要修改的聯絡人姓名
    * @return 0代表一行也沒有更新成功, >0 整數代表的是更新了多少行記錄
    */
   public int update(String newphone , String name){
       SQLiteDatabase db = helper.getWritableDatabase();
       //db.execSQL("update contactinfo set phone =? where name=?", new Object[]{newphone,name});
       ContentValues values = new ContentValues();
       values.put("phone", newphone);
       int rowcount =  db.update("contactinfo", values, "name=?", new String[]{name});
       db.close();
       return rowcount;
   }
   /**
    * 查詢聯絡人的電話號碼
    * @param name 要查詢的聯絡人
    * @return 電話號碼
    */
   public String getPhoneNumber(String name){
       String phone = null;
       SQLiteDatabase db = helper.getReadableDatabase();
       //Cursor  cursor = db.rawQuery("select phone from contactinfo where name=?", new String[]{name});
       Cursor  cursor =  db.query("contactinfo", new String[]{"phone"}, "name=?", new String[]{name}, null, null, null);
       if(cursor.moveToNext()){//如果游標可以移動到下一位,代表就是查詢到了資料
           phone = cursor.getString(0);
       }
       cursor.close();//關閉掉遊標,釋放資源
       db.close();//關閉資料庫,釋放資源
       return phone;
   }
}


.

6、原始碼下載

原始碼分成了兩個module,一個是原生版的,一個是android提供的api版本的。

下載原始碼(Android Studio格式)

以下是兩個module的說明圖片


1083096-af0849ff622df749.png
compare.png

六、資料庫的事務

  • 資料庫的事務有什麼作用? —— 保證某些操作要麼全部成功,要麼都不成功。
  • 資料庫是什麼時候用? 比如金額交易,資料的批量刪除和儲存等。比如現在要進行銀行轉賬,不可能扣了A的錢,但是B沒有收到,然後A的錢就不知道哪裡去了,再比如,現在要備份簡訊,要麼成功備份到磁碟,要麼備份失敗,而不可能在沒有備份成功的情況就把本地給刪除了。
  • 資料庫的事務怎麼用?下面展示一份例子:
        btn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                db.beginTransaction(); // 開啟事務
                try {
                    // 執行刪除操作  db.delete(..........)
                    if (true) {
                        // 在這裡手動丟擲一個異常,讓事務失敗
                        throw new NullPointerException();
                    }
                    // 執行新增操作
                    db.setTransactionSuccessful(); // 事務已經執行成功
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    db.endTransaction(); // 結束事務
                }
            }
        });

1、呼叫SQLiteDatabase的 beginTransaction() 方法來開啟一個事務,然後在一個異常捕獲的程式碼塊中去執行具體的資料庫操作,
2、當所有的操作都完成之後,呼叫 setTransactionSuccessful() 表示事務已經執行成功了
3、最後在finally程式碼塊中呼叫 endTransaction() 來結束事務。

本篇完。

相關文章