Android Study 之 SQLite 了解與基本運用

<font color=FF0000> LZ-Says:給大家推薦一個網站,有興趣可以查閱,想為大家貢獻一點自己的力量也可以投稿,老大審核通過會發表,更好的幫助有需要的人歡迎大家踴躍投稿地址如下:
http://www.123si.org/android

當年 <font color=#FF0000 size=4 >豪放</font> 愛自由,而今 <font color=#DAA520 size=4 >闊步</font> 向前(錢)看~誰還沒個 <font color=#0000FF size=4 >年少輕狂</font> !

1. SQLite 簡介

1-1 SQLite 介紹

SQLite,是一款輕型的數據庫,是遵守ACID的關系型數據庫管理系統,它包含在一個相對小的C庫中。

它設計目標是嵌入式,力求占用資源較低,處理速度較快;它支持Windows/Linux/Unix等等主流的操作系統,同時也支持很多語言(Java,php,.Net等)

總的來說,<font color=#FF0000 size=4 >SQLite是一個軟件庫,實現了自給自足的、無服務器的、零配置的、事務性的 SQL 數據庫引擎</font>

1-2 SQLite 歷史

  1. 2000 -- D. Richard Hipp 設計 SQLite 是為了不需要管理即可操作程序;
  2. 2000 -- 在八月,SQLite1.0 發布 GNU 數據庫管理器(GNU Database Manager);
  3. 2011 -- Hipp 宣布,向 SQLite DB 添加 UNQl 接口,開發 UNQLite(面向文檔的數據庫)

1-3 SQLite 局限性

在 SQLite 中,SQL92 不支持的特性如下所示:

這里寫圖片描述

2. SQLite 優點及缺點

2-1 SQLite 優點

  • 動態數據類型存儲:采用無數據類型,所以可以保存任何類型的數據,會根據存入值自動判斷(<font color=#FF0000 >一般推薦指定類型為好</font>);

  • 輕量級

  • 綠色軟件:核心引擎本身不依賴第三方的軟件,使用它也不需要“安裝”,無需各種瑣碎配置 ;

  • 單一文件:數據庫中所有的信息(比如表、視圖、觸發器、等)都包含在一個文件內,方便移植;

  • 跨平臺/可移植性

  • 支持多語言編程接口

  • 開源,免費

  • 。。。 。。。

2-2 SQLite 缺點

  • 并發訪問的鎖機制:數據庫可能會被寫操作獨占,從而導致其他讀寫操作阻塞或出錯;

  • SQL標準支持不全;

  • 網絡文件系統(NFS)并發讀寫可能會出問題: SQLite文件放置于NFS時,在并發讀寫的情況下可能會出問題(比如數據損壞)。原因<font color=#FF0000 >據說是由于某些NFS的文件鎖實現上有Bug</font>。

3. SQLite 數據類型

我們在上面曾說過,SQLite采用的是動態數據類型存儲,它擁有基本數據類型,同時也關聯親和(Affinity)類型,具體說明如下:

3-1 基本數據類型

這里寫圖片描述

3-2 親和(Affinity)類型

SQLite支持列的親和類型概念。任何列仍然可以存儲任何類型的數據,當數據插入時,該字段的數據將會優先采用親緣類型作為該值的存儲方式。

SQLite目前的版本支持以下五種親緣類型:

這里寫圖片描述

3-3 數據類型名稱以及相應的親和類型:

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

3-4 存儲時間類型技巧

這里寫圖片描述

4. Android 官方提供常用方法

Android提供了創建和使用 SQLite 數據庫的 API 。SQLiteDatabase 代表一個數據庫對象,提供了操作數據庫的一些方法。

下面我們分別去了解下Android為我們提供這些API的常用內容。

4-1 SQLiteOpenHelper 常用方法簡述

  • 1. void onCreate(SQLiteDatabase db): 在首次生成數據庫時才會被調用,在onCreate()方法里可以生成數據庫表結構等相關初始化操作。
  • 2. void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion): 在數據庫的版本發生變化時會被調用,一般在軟件升級時才需改變版本號,或者說由于需求的變化導致不得不對數據庫相關屬性(字段),那么這時候可以在這里面做相關操作。

4-2 SQLiteDatabase 常用方法簡述

<font size=3 >1. 獲取操作數據庫的SQLiteDatabase實例</font>

  • getReadableDatabase()
    先以讀寫方式打開數據庫,如果數據庫的磁盤空間滿了,就會打開失敗,當打開失敗后會繼續嘗試以只讀方式打開數據庫。如果該問題成功解決,則只讀數據庫對象就會關閉,然后返回一個可讀寫的數據庫對象。
  • getWritableDatabase()
    以讀寫方式打開數據庫,一旦數據庫的磁盤空間滿了,數據庫就只能讀而不能寫

繼續深入了解,我們發現他們內部調用同一個方法,同樣是獲取數據庫的SQLiteDatabase實例,只不過二者之間對異常處理的方式不一樣。

這里寫圖片描述

<font size=3 >2. 新增</font>

  • <font color=#FF0000 >long insert(String table, String nullColumnHack, ContentValues values)</font>
  • long insertOrThrow(String table, String nullColumnHack, ContentValues values)
  • long insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm)

官方為我們提供了以上三種方式去實現新增操作,頻繁使用的也就是第一種方式,通過查閱源碼得知,<font color=#FF0000 >insert()和insertOrThrow()最終都會調用insertWithOnConflict()</font>。

為了避免大家說我扯犢子,把找到的證據擺上來:

這里寫圖片描述

這里寫圖片描述

也就是說最后都會通過調用insertWithOnConflict()去做處理,也就是進行新增操作。接下來我們再簡單聊聊參數相關含義,如下:

  • table:要插入數據的表的名稱;

  • values:一個ContentValues對象,類似一個map.通過鍵值對的形式存儲值;

  • conflictAlgon:沖突解決方案。例如當數據表主鍵的唯一性檢測出錯的時候,就會按照該值設定的值進行處理;

  • nullColumnHack:當values參數為空或者里面沒有內容的時候,我們insert是會失敗的(底層數據庫不允許插入一個空行),為了防止這種情況,我們要在這里指定一個 列名,到時候如果發現將要插入的行為空行時,就會將你指定的這個列名的值設為null,然后再向數據庫中插入

方法返回值含義: <font color=#FF0000>方法返回當前插入的索引。</font>

<font size=3 >3. 刪除</font>

  • int delete(String table, String whereClause, String[] whereArgs)

參數解釋如下:
whereClause:條件,為一個字符串;如果多個條件中間使用 and 隔開;
whereArgs:字符串數組,和whereClause配合使用。與條件匹配的值。

<font size=3 >4. 修改</font>

  • int update(String table, ContentValues values, String whereClause, String[] whereArgs)
  • int updateWithOnConflict(String table, ContentValues values,String whereClause, String[] whereArgs, int conflictAlgorithm)
    我們普遍使用的是update()方法,但是它內部會調用updateWithOnConflict()去實現更新操作,有興趣的可以了解了解

<font size=3 >5. 查詢</font>

  • Cursor query(String table, String[] columns, String selection,String[] selectionArgs, String groupBy, String having,String orderBy);
  • Cursor query(boolean distinct, String table, String[] columns,String selection, String[] selectionArgs, String groupBy,String having, String orderBy, String limit);
  • Cursor query(boolean distinct, String table, String[] columns,String selection, String[] selectionArgs, String groupBy,String having, String orderBy, String limit, CancellationSignal cancellationSignal);
  • Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having,String orderBy, String limit);
  • Cursor rawQuery(String sql, String[] selectionArgs);
  • Cursor rawQuery(String sql, String[] selectionArgs, CancellationSignal cancellationSignal);
  • Cursor <font color=#FF0000>rawQueryWithFactory</font>( CursorFactory cursorFactory, String sql, String[] selectionArgs,String editTable);
    查詢最終依然調用了rawQueryWithFactory(),主要的操作還是在這里面實現。其中相關的參數大家可以從字面上理解,這點不得不說谷歌編碼還是很6的

以上的方法都是基于實用官方封裝好的方法,那么有的兄弟問了,我想直接使用SQL語句怎么弄,比較封裝好的內部也是通過拼接SQL去實現功能的,別急,官方在查詢中同樣也提供了SQL語句方式,大家注意看上面,下面簡單介紹下execSQL()~

execSQL(String sql, Object[] bindArgs):方法的第一個參數為SQL語句,第二個參數為SQL語句中占位符參數的值,參數值在數組中的順序要和占位符的位置對應。

通過查看源碼,我們可以得知此方法是void類型,也就是說無返回!大家注意。

這里寫圖片描述

下面為大家舉個小例子,如下:

//省略初始化操作
方法一:指定列名
db.execSQL("insert into stu(stu_name,stu_age,stu_address) values(?,?,?)", new Object[]{"賀大寶",21,"目前在廊坊~"});
方法二:不指定列名
db.execSQL("insert into stu values(?,?,?)", new Object[]{"賀大寶",21,"目前在廊坊~"});
//省略關閉操作

5. 常用 SQL 語句

<font color=#FF0000 size=4>注意:SQL 語句對大小寫不敏感</font>

5-1 基本 SQL 語句

  • insert --- 插入

寫法一: insert into table_name values (值1, 值2,....)

寫法二: insert into table_name values (列1, 列2,...) values(值1, 值2,....)<font color=#FF0000 >(需注意的是,值需要和列一一對應)</font>

  • delete --- 刪除

刪除符合條件數據:delete from table_name where 列名稱=值

刪除表中所有數據:delete from table_name

  • update --- 修改

修改一列:update table_name set 列名稱 = 新值 where 列名稱 = 某值

修改多列:update table_name set 列名稱1 = 新值1,列名稱2 = 新值2 where 列名稱 = 某值

  • select --- 查詢

查詢某表中所有數據:select * from table_name

查詢某表中指定列數據:select 列名稱 from table_name

查詢去除重復數據:select distinct 列名稱 from table_name

  • order by --- 排序

order by 語句用于根據指定的列對結果集進行排序;

order by 語句默認按照升序對記錄進行排序,如果希望按照降序對記錄進行排序,可以使用 desc 關鍵字。
附上一張例子圖:


這里寫圖片描述

5-2 常用 SQL 函數

  • avg() --- 返回數值列的平均值(null 值不包括在計算中)

select avg(column_name) from table_name

  • count() --- 返回指定列的值的數目(null 不計入)

表中的記錄數:select count(*) from table_name

返回指定列的值的數目:select count(column_name) from table_name

  • max() --- 返回一列中的最大值(null 值不包括在計算中)

select max(column_name) from table_name

  • min() --- 返回一列中的最小值(null 值不包括在計算中)

select min(column_name) from table_name

  • sum() --- 返回數值列的總數

select sum(column_name) from table_name

  • first() --- 返回指定的字段中第一個記錄的值

select first(column_name) from table_name

  • last() --- 返回指定的字段中最后一個記錄的值

select last(column_name) from table_name

6. 讓我們愉快的開始擼碼之旅吧~

我們在上面說過,官方為我們提供了 SQLiteOpenHelper 去方便我們簡單,快速創建數據庫以及基本表,那么具體實現又是怎么樣的呢?看下面高能熱量~

6-1 創建DBHelper幫助類

package cn.hlq.sqlitestudy.db;

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

/**
 * Created by HLQ on 2017/4/7
 * 創建數據庫(初始化)幫助類
 * 現在創建一個學生表,表中含有編號,姓名,年齡,地址
 */
public class DBHelper extends SQLiteOpenHelper {

    /**
     * 數據庫名稱
     */
    private static final String DATABASE_NAME="sqlite_study.db";
    /**
     * 數據庫版本號
     */
    private static final int DATABASE_VERSION=1;

    /**
     * 初始化設置數據庫名稱,數據庫版本號
     * @param context
     */
    public DBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * 鍵明其意,在創建的時候會調用,而且只會調用一次
     * @param db
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        // 創建學生表
        db.execSQL(SQLManager.SQL_CREATE_TABLE_STU);
    }

    /**
     * 數據庫有更新時調用
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

6-2 創建SQLManager管理類

為了后期方便拓展,我們可以將SQL語句歸為一個類,單獨管理

package cn.hlq.sqlitestudy.db;

/**
 * Created by HLQ on 2017/4/7
 * 這里為了避免累贅,簡單附上創建學生表的SQL語句,具體詳情大家可直接查看Demo源碼
 */

public class SQLManager {

    /**
     * 創建學生表
     */
    public static final String SQL_CREATE_TABLE_STU="create table if not exists stu (stu_id integer primary key autoincrement,stu_name varchar(15),stu_age integer,stu_address varchar(50))";

}

6-3 創建DBManager管理類,實現CRUD

package cn.hlq.sqlitestudy.db;

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

import java.util.ArrayList;
import java.util.List;

import cn.hlq.sqlitestudy.entity.Student;

/**
 * Created by HLQ on 2017/4/7
 */

public class DBManager {

    private Context context;
    private DBHelper helper;
    private SQLiteDatabase db;

    /**
     * 構造方法初始化
     *
     * @param context
     */
    public DBManager(Context context) {
        this.context = context;
        this.helper = new DBHelper(context);
        this.db = helper.getWritableDatabase();
    }

    /**
     * 新增一條數據
     *
     * @param stu
     * @return
     */
    public long insertDB(Student stu) {
        ContentValues values = new ContentValues();
        values.put("stu_name", stu.getStuName());
        values.put("stu_age", stu.getStuAge());
        values.put("stu_address", stu.getStuAddress());
        long rowsNum = 0;
        try {
            rowsNum = db.insert("stu", null, values);
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("HLQ_Struggle", "insert error:" + e.getMessage());
        } finally {
            if (db != null) {
                db.close();
            }
        }
        return rowsNum;
    }

    /**
     * 刪除單條數據
     *
     * @param stuName
     * @return
     */
    public int deleteDB(String stuName) {
        int rowsNum = 0;
        try {
            rowsNum = db.delete("stu", "stu_name=?", new String[]{stuName});
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("HLQ_Struggle", "delete error:" + e.getMessage());
        } finally {
            if (db != null) {
                db.close();
            }
        }
        return rowsNum;
    }

    /**
     * 刪除多條數據
     *
     * @param stuName
     * @param stuAge
     * @return
     */
    public int deleteDBForWhere(String stuName, int stuAge) {
        int rowsNum = 0;
        try {
            rowsNum = db.delete("stu", "stu_name=? and stu_age=?", new String[]{stuName, stuAge + ""});
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("HLQ_Struggle", "delete more where error:" + e.getMessage());
        } finally {
            if (db != null) {
                db.close();
            }
        }
        return rowsNum;
    }

    /**
     * 根據姓名修改
     *
     * @param stuName
     * @return
     */
    public int updateDB(String stuName) {
        ContentValues values = new ContentValues();
        values.put("stu_name", "Test");
        int rowsNum = 0;
        try {
            rowsNum = db.update("stu", values, "stu_name=?", new String[]{stuName});
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("HLQ_Struggle", "update error:" + e.getMessage());
        } finally {
            if (db != null) {
                db.close();
            }
        }
        return rowsNum;
    }

    /**
     * 查詢所有數據
     *
     * @return
     */
    public List<Student> queryStu() {
        List<Student> stuList = new ArrayList<>();
        Cursor cursor = null;
        try {
            cursor = db.query("stu", null, null, null, null, null, null);
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    Student stu = new Student();
                    stu.setStuName(cursor.getString(cursor.getColumnIndex("stu_name")));
                    stu.setStuAge(cursor.getInt(cursor.getColumnIndex("stu_age")));
                    stu.setStuAddress(cursor.getString(cursor.getColumnIndex("stu_address")));
                    stuList.add(stu);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("HLQ_Struggle", "query error:" + e.getMessage());
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            if (db != null) {
                db.close();
            }
        }
        return stuList;
    }

}

都說眼見為實,那么讓我們一起看看運行結果。

運行結果大展示

  • 新增一條數據
這里寫圖片描述

新增成功,看看數據庫中數據是否正確。

這里寫圖片描述
  • 根據一個條件刪除數據
這里寫圖片描述
這里寫圖片描述

刪除成功,看看數據庫中數據是否正確。

這里寫圖片描述
  • 根據多個條件刪除數據

首先查看下數據庫目前存儲數據內容

這里寫圖片描述

接下來要對姓名為“張三”,年齡為“18”的數據進行刪除,結果如下:

這里寫圖片描述

再次查看數據庫中數據

這里寫圖片描述
  • 根據條件修改數據
這里寫圖片描述
這里寫圖片描述

修改成功,看看數據庫中數據是否正確

這里寫圖片描述
  • 查詢結果展示
這里寫圖片描述

7. 使用注意項以及相關技巧

7-1 創建表以及屬性(字段)需注意避免使用 <font color=#FF0000 >關鍵字</font>

官方給出關鍵字如下:

這里寫圖片描述

大家使用過程中可以取第一位前綴或者單詞,例如,Stu表中id可以寫成 s_id or stu_id,這樣感覺一下子就明確多了。

7-2 創建表使用關鍵字 <font color=#FF0000 >if not exists</font>

使用普通創建表可能會返回創建失敗,原因可能是表已存在,而是用if not exists,如果不存在才會創建,保證程序健壯性。

7-3 讀取或者查詢完之后 <font color=#FF0000 >記得關閉游標或者數據庫,防止內存泄漏</font>

8. 代碼查看及下載

GitHub 查看地址:https://github.com/HLQ-Struggle/SQLiteStudy

Demo 下載地址:http://download.csdn.net/detail/u012400885/9807179

9. 參考資料

感謝如下各位同仁奉獻(前人栽樹,后人乘涼),Thanks:

1. http://baike.baidu.com/link?url=k3hYvLxllkitqVHdjF-phxvOzaeGtM6YBLwjRYrFeRJXJIiY8LGrcvYxpfBX4ooFGWUlPDbHhNifVp_Zn30LTK ;

2. http://www.runoob.com/sqlite/sqlite-tutorial.html ;

3. http://blog.sina.com.cn/s/blog_8cfbb9920100zetj.html

4. http://blog.knowsky.com/185331.htm

5. http://www.w3school.com.cn/sql/index.asp ;

6. http://blog.csdn.net/imxilife/article/details/45620009

7. http://blog.csdn.net/primer_programer/article/details/28513919;

8. http://www.educity.cn/wenda/586383.html;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容