資料儲存與存取之-初見SQLite資料庫


本節引言:

本節我們繼續來學習Android資料儲存與存取的第三種方式:SQLite資料庫,和其他的SQL資料庫不同, 我們並不需要在手機上另外安裝一個資料庫軟體,Android系統已經整合了這個資料庫,我們無需像 使用其他資料庫軟體(Oracle,MSSQL,MySql等)又要安裝,然後完成相關配置,又要改連接埠之類的! 引言就說這麼多,接下來我們來學習下這個東西~


1.基本概念

1)SQLite是什麼?為什麼要用SQLite? SQLite有什麼特色?

答:下面請聽小豬娓娓道來:

①SQLite是一個輕量級的關係型資料庫,運算速度快,佔用資源少,很適合在移動設備上使用, 不僅支援標準SQL語法,還遵循ACID(資料庫事務)原則,無需帳號,使用起來非常方便!

②前面我們學習了使用檔案與SharedPreference來保存資料,但是在很多情況下, 文件不一定是有效的,如多執行緒並發存取是相關的;app要處理可能變化的複雜資料結構等等! 例如銀行的存錢跟取錢!使用前兩者就會顯得很無力或繁瑣,資料庫的出現可以解決這種問題, 而Android又給了我們這樣一個輕量級的SQLite,為何不用?

③SQLite支援五種資料型別:NULL,INTEGER,REAL(浮點數),TEXT(字串文字)和BLOB(二進位物件) 雖然只有五種,但是對於varchar,char等其他資料類型都是可以保存的;因為SQLite有個最大的特點:你可以各種資料類型的資料保存到任何欄位中而不用關心欄位聲明的資料型別是什麼,比如你 可以在Integer類型的欄位中存放字串,當然除了宣告為主鍵INTEGER PRIMARY KEY的欄位只能夠儲存64位元整數! 另外, SQLite 在解析CREATE TABLE 語句時, 會忽略CREATE TABLE 語句中跟在欄位名稱後面的資料型別資訊如下面語句會忽略name欄位的型別資訊:CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))

#小結下特點:

SQlite透過檔案來保存資料庫,一個檔案就是一個資料庫,資料庫中又包含多個表格,表格裡又有 多條記錄,每個記錄由多個欄位構成,每個欄位有對應的,每個值我們可以指定類型,也可以不指定 類型(主鍵除外)

PS:對了,Android內建的SQLite是SQLite 3版本的~

2)幾個相關的類別:

嘿嘿,學習一些新東西的時候,最不喜歡的莫過於遇到一些新名詞,對吧,我們先來說下幾個 我們在使用資料庫時用到的三個類別:

  • SQLiteOpenHelper:抽象類,我們透過繼承該類,然後重寫資料庫建立以及更新的方法, 我們也可以透過該類別的物件取得資料庫實例,或關閉資料庫!
  • SQLiteDatabase:資料庫存取類別:我們可以透過該類別的物件來對資料庫做一些增刪改查的操作
  • Cursor:遊標,有點類似JDBC裡的resultset,結果集!可以簡單理解為指向資料庫中某 一個記錄的指針!

2.使用SQLiteOpenHelper類別建立資料庫與版本管理

對於涉及資料庫的app,我們不可能手動地去為他建立資料庫文件,所以需要在第一次啟用app 的時候就創建好資料庫表;而當我們的應用進行升級需要修改資料庫表的結構時,這個時候就需要 對資料庫表進行更新了;對於這兩個操作,安卓給我們提供了SQLiteOpenHelper的兩個方法,onCreate( )與onUpgrade( )來實作

方法解析

  • #onCreate(database):首次使用軟體時產生資料庫表
  • onUpgrade(database,oldVersion,newVersion):在資料庫的版本發生變化時會被調用, 一般在軟體升級時才需改變版本號,而資料庫的版本是由程式設計師控制的,假設資料庫現在的 版本是1,由於業務的變更,修改了資料庫表結構,這時候就需要升級軟體,升級軟體時希望 更新用戶手機裡的資料庫表結構,為了實現這個目的,可以把原來的資料庫版本設定為2 或其他與舊版號不同的數字即可!

程式碼範例

public class MyDBOpenHelper extends SQLiteOpenHelper {
    public MyDBOpenHelper(Context context, String name, CursorFactory factory,
            int version) {super(context, "my.db", null, 1); }
    @Override
    //数据库第一次创建时被调用
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE person(personid INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20))");
        
    }
    //软件版本号发生改变时调用
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("ALTER TABLE person ADD phone VARCHAR(12) NULL");
    }
}

#程式碼解析

##上述程式碼第一次啟動應用,我們會建立這個my.db的文件,並且會執行onCreate()裡的方法, 建立Person的表,他又兩個字段,主鍵personId和name字段;接著如我我們修改db的版本 號,那麼下次啟動就會呼叫onUpgrade()裡的方法,往表中再插入一個欄位!另外這裡是插入 一個字段,所以資料不會遺失,如果是重建表的話,表中的資料會全部遺失,下一節我們會 來教大家如何解決這個問題!

流程小結

  • Step 1:自訂一個類別繼承SQLiteOpenHelper類別
  • Step 2:在該類別的建構方法的super中設置好要建立的資料庫名稱,版本號
  • Step 3:重寫onCreate( )方法建立表格結構
  • Step 4:重寫onUpgrade( )方法定義版本號改變後執行的操作

3.如何查看我們產生的db檔

當我們呼叫上面的MyDBOpenhelper的物件的getWritableDatabase()就會在下述目錄下建立我們的db 資料庫檔案:

8.png

我們發現資料庫有兩個,前者是我們建立的資料庫,而後者則是為了能讓資料庫支援交易而產生的 臨時的日誌檔!一般的大小是0位元組! 而在File Explorer裡我們確是打不開檔案的,連txt都打不開,何況是.db! 所以下面給大家兩個路選:

  • 1.先匯出來,然後用SQLite的圖形化工具查看
  • 2.配置adb環境變數後,透過adb shell來查看(命令列,裝比利器)!

嗯,接著跟大家示範上述兩種方法,選自己喜歡的一種就可以了~~


方法1:使用SQLite圖形化工具檢視db檔

這類軟體很多,筆者用的是SQLite Expert Professional,當然你也可以使用其他工具 又需要的可以下載:SQLiteExpert.zip

把我們的db檔案匯出到電腦桌面,開啟SQLiteExpert,介面如下:

1.png

別問我怎麼玩,導入db後自己慢慢玩,用法很簡單,不懂百度~


#至於方法二,本來是想試試的,後來發現sqlite指令找不到,試了幾次就算了, 後面用到在細扣,有興趣可以找下郭霖的《第一行程式碼-Android》以流程圖試試! 這裡只貼前面的一部分,指令部分自己看書!

方法2:adb shell指令行帶你裝逼帶你飛

#1.設定SDK環境變數:

右鍵我的電腦 ——> 進階系統設定 -> 環境變數 -> 新系統變數 -> 把SDK的platform-tools路徑拷貝下: 例如筆者的:C:\Software\Coding\android-sdks-as\platform-tools

2.png

##確定,然後再找到Path的環境變量,編輯,然後在結尾加上:

%SDK_HOME%;#

3.png

然後開啟指令行,輸入adb,唰唰唰一堆東西,就表示配置成功了!

——————重點——————: 在執行後續命令列指令之前,針對你的測試的機器可能有幾種: 1.原生模擬器:那行,你跳過這裡,繼續往下 2.Genymotion模擬器:沒戲,Genymotion Shell執行不了下述指令 3.真機(已root):那你開啟File Explorer看看data/data/目錄下有東西沒?沒麼? 下面提供一個方法,就是先裝個RE檔案管理器,然後授予RE Root權限,接著來到根目錄: 然後長按data目錄,會跳出這樣的對話框:

4.png10.png

5.png

#接著等他慢慢修改權限,修改完畢之後,我們再次開啟DDMS的File Explorer,我們可以看到:

6.png

好的,可以看到data/data裡的東西了! ——————————————————————

2.進入adb shell,接著鍵入下述指令,來到我們app的databases目錄下:

7.png

#接著依序輸入下述指令:

  • sqlite3 my.db :開啟資料庫檔案
  • .table 查看資料庫中有哪些表 接著你直接輸入資料庫語句就可以了,例如查詢:Select * from person
  • .schema:查看建表語句
  • .quit :退出資料庫的編輯
  • .exit:退出裝置控制台

#...因為system/bin/sh sqlite3: not found,這個問題,後面Sqlite指令的都用不了, 要看效果圖就自行查詢郭大俠的書吧~而下面我們還是先匯出db文件,再用圖形化的 資料庫工具來查看!


4.使用Android提供的API操作SQLite

假如你沒學過資料庫相關的語法,或是你懶,不想寫資料庫語法,就可以使用Android給我們 提供的操作資料庫的一些API方法,下面我們寫個簡單的範例來掩飾下這些API的用法!

程式碼範例

運行效果圖

8.gif

實現程式碼

佈局過於簡單,就四個Button,就不貼了,直接貼上MainActivity.java的程式碼:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Context mContext;
    private Button btn_insert;
    private Button btn_query;
    private Button btn_update;
    private Button btn_delete;
    private SQLiteDatabase db;
    private MyDBOpenHelper myDBHelper;
    private StringBuilder sb;
    private int i = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = MainActivity.this;
        myDBHelper = new MyDBOpenHelper(mContext, "my.db", null, 1);
        bindViews();
    }

    private void bindViews() {
        btn_insert = (Button) findViewById(R.id.btn_insert);
        btn_query = (Button) findViewById(R.id.btn_query);
        btn_update = (Button) findViewById(R.id.btn_update);
        btn_delete = (Button) findViewById(R.id.btn_delete);

        btn_query.setOnClickListener(this);
        btn_insert.setOnClickListener(this);
        btn_update.setOnClickListener(this);
        btn_delete.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        db = myDBHelper.getWritableDatabase();
        switch (v.getId()) {
            case R.id.btn_insert:
                ContentValues values1 = new ContentValues();
                values1.put("name", "呵呵~" + i);
                i++;
                //参数依次是:表名,强行插入null值得数据列的列名,一行记录的数据
                db.insert("person", null, values1);
                Toast.makeText(mContext, "插入完毕~", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn_query:
                sb = new StringBuilder();
                //参数依次是:表名,列名,where约束条件,where中占位符提供具体的值,指定group by的列,进一步约束
                //指定查询结果的排序方式
                Cursor cursor = db.query("person", null, null, null, null, null, null);
                if (cursor.moveToFirst()) {
                    do {
                        int pid = cursor.getInt(cursor.getColumnIndex("personid"));
                        String name = cursor.getString(cursor.getColumnIndex("name"));
                        sb.append("id:" + pid + ":" + name + "\n");
                    } while (cursor.moveToNext());
                }
                cursor.close();
                Toast.makeText(mContext, sb.toString(), Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn_update:
                ContentValues values2 = new ContentValues();
                values2.put("name", "嘻嘻~");
                //参数依次是表名,修改后的值,where条件,以及约束,如果不指定三四两个参数,会更改所有行
                db.update("person", values2, "name = ?", new String[]{"呵呵~2"});
                break;
            case R.id.btn_delete:
                //参数依次是表名,以及where条件与约束
                db.delete("person", "personid = ?", new String[]{"3"});
                break;
        }
    }
}

5.使用SQL語句操作資料庫

##當然,你可能已經學過SQL,會寫相關的SQL語句,而且不想用Android提供的這些API, 你可以直接使用SQLiteDatabase給我們提供的相關方法:

  • execSQL(SQL,Object[]):使用有佔位符的SQL語句,這個是執行修改資料庫內容的sql語句所用的
  • rawQuery(SQL,Object[]):使用帶有佔位符的SQL查詢操作 另外前面忘了介紹下Curosr這個東西以及相關屬性,這裡補充下: ——Cursor物件有點類似JDBC中的ResultSet,結果集!使用差不多,提供一下方法移動查詢結果的記錄指標:
  • move(offset) :指定向上或向下移動的行數,整數表示向下移動;負數表示向上移動!
  • moveToFirst():指標移動到第一行,成功返回true,也說明有資料
  • moveToLast():指標移動到最後一樣,成功返回true;
  • moveToNext():指標移動到下一行,成功回傳true,表示還有元素!
  • moveToPrevious():移到上一筆記錄
  • #getCount( )取得總得資料條數
  • # isFirst():是否為第一筆記錄
  • isLast():是否為最後一項
  • moveToPosition(int) :移動到指定行
使用程式碼範例:

1.插入資料:

public void save(Person p)
{
    SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
    db.execSQL("INSERT INTO person(name,phone) values(?,?)",
                new String[]{p.getName(),p.getPhone()});
}

2.刪除資料:

public void delete(Integer id)
{
    SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
    db.execSQL("DELETE FROM person WHERE personid = ?",
                new String[]{id});
}

3.修改資料:

public void update(Person p)
{
    SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
    db.execSQL("UPDATE person SET name = ?,phone = ? WHERE personid = ?",
        new String[]{p.getName(),p.getPhone(),p.getId()});
}

4.查詢資料:

public Person find(Integer id)
{
    SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
    Cursor cursor =  db.rawQuery("SELECT * FROM person WHERE personid = ?",
            new String[]{id.toString()});
    //存在数据才返回true
    if(cursor.moveToFirst())
    {
        int personid = cursor.getInt(cursor.getColumnIndex("personid"));
        String name = cursor.getString(cursor.getColumnIndex("name"));
        String phone = cursor.getString(cursor.getColumnIndex("phone"));
        return new Person(personid,name,phone);
    }
    cursor.close();
    return null;
}

5.資料分頁:

public List<Person> getScrollData(int offset,int maxResult)
{
    List<Person> person = new ArrayList<Person>();
    SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
    Cursor cursor =  db.rawQuery("SELECT * FROM person ORDER BY personid ASC LIMIT= ?,?",
        new String[]{String.valueOf(offset),String.valueOf(maxResult)});
    while(cursor.moveToNext())
    {
        int personid = cursor.getInt(cursor.getColumnIndex("personid"));
        String name = cursor.getString(cursor.getColumnIndex("name"));
        String phone = cursor.getString(cursor.getColumnIndex("phone"));
        person.add(new Person(personid,name,phone)) ;
    }
    cursor.close();
    return person;
}

6.查詢記錄數:

public long getCount()
{
    SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
    Cursor cursor =  db.rawQuery("SELECT COUNT (*) FROM person",null);
    cursor.moveToFirst();
    long result = cursor.getLong(0);
    cursor.close();
    return result;      
}

PS:除了上面取得條數的方法外還可以使用cursor.getCount()方法獲得資料的條數, 但是SQL語句要改改!例如SELECT * FROM person;


本節小結:

本節介紹了Android內建SQLite的基本用法,還是比較簡單的,下一節再來研究點稍微 進階一點的東西,SQLite事務,應用更新資料庫裡資料怎麼處理,以及資料庫儲存大二進位文件 的方法!好的,本節就到這裡~