Android Study 之 SQLite 了解與基本運用

<font color=FF0000> LZ-Says:給大家推薦一個網(wǎng)站,有興趣可以查閱,想為大家貢獻一點自己的力量也可以投稿,老大審核通過會發(fā)表,更好的幫助有需要的人歡迎大家踴躍投稿地址如下:
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,是一款輕型的數(shù)據(jù)庫,是遵守ACID的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),它包含在一個相對小的C庫中。

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

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

1-2 SQLite 歷史

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

1-3 SQLite 局限性

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

這里寫圖片描述

2. SQLite 優(yōu)點及缺點

2-1 SQLite 優(yōu)點

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

  • 輕量級

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

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

  • 跨平臺/可移植性

  • 支持多語言編程接口

  • 開源,免費

  • 。。。 。。。

2-2 SQLite 缺點

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

  • SQL標準支持不全

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

3. SQLite 數(shù)據(jù)類型

我們在上面曾說過,SQLite采用的是動態(tài)數(shù)據(jù)類型存儲,它擁有基本數(shù)據(jù)類型,同時也關(guān)聯(lián)親和(Affinity)類型,具體說明如下:

3-1 基本數(shù)據(jù)類型

這里寫圖片描述

3-2 親和(Affinity)類型

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

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

這里寫圖片描述

3-3 數(shù)據(jù)類型名稱以及相應(yīng)的親和類型:

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

3-4 存儲時間類型技巧

這里寫圖片描述

4. Android 官方提供常用方法

Android提供了創(chuàng)建和使用 SQLite 數(shù)據(jù)庫的 API 。SQLiteDatabase 代表一個數(shù)據(jù)庫對象,提供了操作數(shù)據(jù)庫的一些方法。

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

4-1 SQLiteOpenHelper 常用方法簡述

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

4-2 SQLiteDatabase 常用方法簡述

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

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

繼續(xù)深入了解,我們發(fā)現(xiàn)他們內(nèi)部調(diào)用同一個方法,同樣是獲取數(shù)據(jù)庫的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)

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

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

這里寫圖片描述

這里寫圖片描述

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

  • table:要插入數(shù)據(jù)的表的名稱;

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

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

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

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

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

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

參數(shù)解釋如下:
whereClause:條件,為一個字符串;如果多個條件中間使用 and 隔開;
whereArgs:字符串數(shù)組,和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()方法,但是它內(nèi)部會調(diào)用updateWithOnConflict()去實現(xiàn)更新操作,有興趣的可以了解了解

<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);
    查詢最終依然調(diào)用了rawQueryWithFactory(),主要的操作還是在這里面實現(xiàn)。其中相關(guān)的參數(shù)大家可以從字面上理解,這點不得不說谷歌編碼還是很6的

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

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

通過查看源碼,我們可以得知此方法是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,"目前在廊坊~"});
//省略關(guān)閉操作

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 >(需注意的是,值需要和列一一對應(yīng))</font>

  • delete --- 刪除

刪除符合條件數(shù)據(jù):delete from table_name where 列名稱=值

刪除表中所有數(shù)據(jù):delete from table_name

  • update --- 修改

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

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

  • select --- 查詢

查詢某表中所有數(shù)據(jù):select * from table_name

查詢某表中指定列數(shù)據(jù):select 列名稱 from table_name

查詢去除重復數(shù)據(jù):select distinct 列名稱 from table_name

  • order by --- 排序

order by 語句用于根據(jù)指定的列對結(jié)果集進行排序;

order by 語句默認按照升序?qū)τ涗涍M行排序,如果希望按照降序?qū)τ涗涍M行排序,可以使用 desc 關(guān)鍵字。
附上一張例子圖:


這里寫圖片描述

5-2 常用 SQL 函數(shù)

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

select avg(column_name) from table_name

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

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

返回指定列的值的數(shù)目: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() --- 返回數(shù)值列的總數(shù)

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 去方便我們簡單,快速創(chuàng)建數(shù)據(jù)庫以及基本表,那么具體實現(xiàn)又是怎么樣的呢?看下面高能熱量~

6-1 創(chuàng)建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
 * 創(chuàng)建數(shù)據(jù)庫(初始化)幫助類
 * 現(xiàn)在創(chuàng)建一個學生表,表中含有編號,姓名,年齡,地址
 */
public class DBHelper extends SQLiteOpenHelper {

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

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

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

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

    }
}

6-2 創(chuàng)建SQLManager管理類

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

package cn.hlq.sqlitestudy.db;

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

public class SQLManager {

    /**
     * 創(chuàng)建學生表
     */
    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 創(chuàng)建DBManager管理類,實現(xiàn)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;

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

    /**
     * 新增一條數(shù)據(jù)
     *
     * @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;
    }

    /**
     * 刪除單條數(shù)據(jù)
     *
     * @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;
    }

    /**
     * 刪除多條數(shù)據(jù)
     *
     * @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;
    }

    /**
     * 根據(jù)姓名修改
     *
     * @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;
    }

    /**
     * 查詢所有數(shù)據(jù)
     *
     * @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;
    }

}

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

運行結(jié)果大展示

  • 新增一條數(shù)據(jù)
這里寫圖片描述

新增成功,看看數(shù)據(jù)庫中數(shù)據(jù)是否正確。

這里寫圖片描述
  • 根據(jù)一個條件刪除數(shù)據(jù)
這里寫圖片描述
這里寫圖片描述

刪除成功,看看數(shù)據(jù)庫中數(shù)據(jù)是否正確。

這里寫圖片描述
  • 根據(jù)多個條件刪除數(shù)據(jù)

首先查看下數(shù)據(jù)庫目前存儲數(shù)據(jù)內(nèi)容

這里寫圖片描述

接下來要對姓名為“張三”,年齡為“18”的數(shù)據(jù)進行刪除,結(jié)果如下:

這里寫圖片描述

再次查看數(shù)據(jù)庫中數(shù)據(jù)

這里寫圖片描述
  • 根據(jù)條件修改數(shù)據(jù)
這里寫圖片描述
這里寫圖片描述

修改成功,看看數(shù)據(jù)庫中數(shù)據(jù)是否正確

這里寫圖片描述
  • 查詢結(jié)果展示
這里寫圖片描述

7. 使用注意項以及相關(guān)技巧

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

官方給出關(guān)鍵字如下:

這里寫圖片描述

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

7-2 創(chuàng)建表使用關(guān)鍵字 <font color=#FF0000 >if not exists</font>

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

7-3 讀取或者查詢完之后 <font color=#FF0000 >記得關(guān)閉游標或者數(shù)據(jù)庫,防止內(nèi)存泄漏</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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容