一篇好文之Android數據庫 SQLite全解析

在這裏插入圖片描述
這篇文章是數據庫系列篇文章的第一篇,主要講Android Sqlite數據庫存儲,後面陸續出GreenDao,LitePal, Realm,wcdb的文章,一如既往,如果遇到任何關於Android中SQLite的問題,都可以直接在我的文章底部留言,或者直接在我的公衆號aserbao留言,文章會持續更新,希望這篇文章能爲大家提供到幫助!如果覺得文章對你有用,就幫忙點個贊,若覺得文章寫得不好之處望指出,必將加以修正!
在這裏插入圖片描述

這篇文章主要講SQlite數據庫存儲,從數據庫建表到數據庫的增刪改查操作,再到數據庫升級操作,最後是文章總結及參考鏈接

項目效果地址:

在這裏插入圖片描述
AserbaosAndroid

SQlite

1. 創建數據庫

Android中使用SQlite,需要自己創建庫,建表,添加數據!好在Android中提供了SQLiteOpenHelper類來幫助創建使用數據庫,我們只需要繼承這個類就可以實現數據庫的創建和對數據的操作了!Android 中創建數據庫需要通過如下幾部:

  1. 創建一個類繼承SQLiteOpenHelper,需要實現其三個方法:
    • 構造方法:需要給SQLiteOpenHelper傳遞四個參數:(上下文,數據庫名,遊標工廠(通常爲null),當前數據庫版本號);
    • onCreate(): 表的創建,初始化操作
    • onUpgrade(): 表升級
    • 還有:onDowngrade(): 表降級,onOpen:每次打開數據庫,onBeforeDelete:
public class ThingManagerDBOpenHelper extends SQLiteOpenHelper {
    public static final String DB_NAME = "mysql.db";
    private static final int VERSION = 1;
    public ThingManagerDBOpenHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS "
                + ThingDBController.TABLE_NAME
                + String.format(
                "("
                        + "%s INTEGER PRIMARY KEY AUTOINCREMENT, "
                        + "%s VARCHAR, "
                        + "%s INTEGER"
                        +")"
                , ThingManagerDBModel.ID
                , ThingManagerDBModel.MESSAGE
                , ThingManagerDBModel.TIME
        )) ;
    }


    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
       ……
    }
 
}

說下SQlite的數據類型 :

作用
NULL: 這個值爲空值
VARCHAR(n): 長度不固定且其最大長度爲 n 的字串,n不能超過 4000。
CHAR(n): 長度固定爲n的字串,n不能超過 254。
INTEGER: 值被標識爲整數,依據值的大小可以依次被存儲爲1,2,3,4,5,6,7,8.
REAL: 所有值都是浮動的數值,被存儲爲8字節的IEEE浮動標記序號.
TEXT: 值爲文本字符串,使用數據庫編碼存儲(TUTF-8, UTF-16BE or UTF-16-LE).
BLOB: 值是BLOB數據塊,以輸入的數據格式進行存儲。如何輸入就如何存儲,不改 變格式。
DATA: 包含了 年份、月份、日期。
TIME: 包含了 小時、分鐘、秒。

2. 創建一個數據庫存儲對象

public final class ThingManagerDBModel {
    public static final String TABLE_NAME = "mysql_thing";
    public static final String ID = "_id";
    public static final String MESSAGE = "message";
    public static final String TIME = "time";

    int id;
    String message;
    long time;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public ContentValues toContentValues(){
        ContentValues cv = new ContentValues();
        cv.put(MESSAGE, message);
        cv.put(TIME, time);
        return cv;
    }
}

3. 創建一個數據庫控制器實現增刪改查

1. 增

  1. 使用Android APi來實現增加數據
 public boolean insertApi(int id, String message, long time){
        ThingManagerDBModel dbModel = new ThingManagerDBModel();
        if(id >= 0) {
            dbModel.id = id;
        }
        dbModel.message = message;
        dbModel.time = time;
        final boolean success = db.insert(TABLE_NAME, null, dbModel.toContentValues()) != -1;
        dbClose();
        return success;
    }
  1. 使用Sql語句實現增加數據
 public void insertRaw(String message, long time){
        String sql = " insert into " + TABLE_NAME + "(message,time) values(?,?)";
        Object[] args = {message,time};
        db.execSQL(sql,args);
        dbClose();
    }

2. 刪

  1. 使用Api
  public boolean deleteApi(String whereClause,String[] whereArgs){
        boolean sucess = db.delete(TABLE_NAME, whereClause,whereArgs) != -1;
        dbClose();
        return sucess;
    }
  1. 使用Sql語句
 public void deleteRaw(String message){
        String sql = "delete from "+TABLE_NAME + " where message = ?";
        Object[] args = {message};
        db.execSQL(sql,args);
        dbClose();
    }

3. 改

  1. 使用api
public boolean updateApi(int id,String message,long time){
        ThingManagerDBModel dbModel = new ThingManagerDBModel();
        if(id >= 0) {
            dbModel.id = id;
        }
        dbModel.message = message;
        dbModel.time = time;
        boolean success = db.update(TABLE_NAME, dbModel.toContentValues(), "_id = ?", new String[]{String.valueOf(id)}) != -1;
        dbClose();
        return success;
    }
  1. 使用Sql條件修改
public void updateRaw(int id,String message,long time){
        String sql = "update "+TABLE_NAME + " set message = ?,time = ? where _id = ?";
        Object[] args = {message,time,id};
        db.execSQL(sql,args);
        dbClose();
    }

4. 查

  1. 使用Api
/**
     * @param cloums 要查詢的字段
     * @param selection 查詢條件
     * @param selectionArgs 填充查詢條件的值
     * @return
     */
    public List<Thing> queryApi(String[] cloums,String selection,String[]  selectionArgs){
        try {
            Cursor c = db.query(TABLE_NAME, cloums, selection, selectionArgs, null, null, null);
            ArrayList<Thing> arrayList = new ArrayList<>();
            if (!c.moveToLast()) {
                return arrayList;
            }
            do {
                Thing model = new Thing();
                model.setId(c.getInt(c.getColumnIndexOrThrow(ThingManagerDBModel.ID)));
                model.setTime(c.getLong(c.getColumnIndexOrThrow(ThingManagerDBModel.TIME)));
                model.setMessage(c.getString(c.getColumnIndexOrThrow(ThingManagerDBModel.MESSAGE)));
                arrayList.add(model);
            } while (c.moveToPrevious());
            c.close();
            return arrayList;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            dbClose();
        }

        return new ArrayList<>();
    }

  1. 使用sql條件
 public List<Thing> queryRawById(String  id){
 	//cursor獲取的一定是在這裏申明的查詢條件值
        String sql = "select _id,message,time from "+TABLE_NAME + " where _id = ?";
        Cursor cursor = db.rawQuery(sql, new String[]{id});
        ArrayList<Thing> arrayList = new ArrayList<>();
        if (!cursor.moveToLast()) {
            return arrayList;
        }
        do {
            Thing model = new Thing();
            model.setId(cursor.getInt(cursor.getColumnIndex(ThingManagerDBModel.ID)));
            model.setTime(cursor.getLong(cursor.getColumnIndex(ThingManagerDBModel.TIME)));
           model.setMessage(cursor.getString(cursor.getColumnIndexOrThrow(ThingManagerDBModel.MESSAGE)));
            arrayList.add(model);
        } while (cursor.moveToPrevious());
        cursor.close();
        dbClose();
        return arrayList;
    }

4. 數據庫的升級

在開發的過程中,我們避免不了需要修改數據庫結構,添加字段或者刪除字段!我們通過增加傳遞給SQLiteOpenHelper的版本值,得到onUpgrade方法的回調來實現對數據庫的操作來進行升級。這裏需要注意的是,如果用戶安裝版本1之後直接跳裝版本3,這時候我們就需要在onUpgrade方法中通過oldVersion來進行每一個版本的逐漸升級,方法如下:


public class ThingManagerDBOpenHelper extends SQLiteOpenHelper {
    public static final String DB_NAME = "mysql.db";
    private static final int VERSION = 3;
    ……
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion){
            case 1:
                upToDbVersion2(db);
            case 2:
                upToDbVersion3(db);
            default:
              break;
        }
    }
    public void upToDbVersion2(SQLiteDatabase db){
        db.execSQL("ALTER TABLE " + ThingDBController.TABLE_NAME + " ADD COLUMN add_user_name text");
    }

    public void upToDbVersion3(SQLiteDatabase db) {
        ContentValues values = new ContentValues();
        values.put("message", "版本升級後的數據");
        db.update(ThingDBController.TABLE_NAME, values, null, null);
    }
}

問題

查詢問題

Cursor只會擁有你查詢的數據,如果在查詢條件裏面沒有申請,Cursor裏面就不會包含這個值,就會出現如下異常:
錯誤寫法:

 String sql = "select message,time from "+TABLE_NAME + " where _id = ?";//條件裏面沒有申請查詢_id的值
 ……something……
 model.setId(cursor.getInt(cursor.getColumnIndex(ThingManagerDBModel.ID)));//在這裏提取會報異常

正確寫法:

 String sql = "select  _id,message,time from "+TABLE_NAME + " where _id = ?";//條件裏面沒有申請查詢_id的值
 ……something……
 model.setId(cursor.getInt(cursor.getColumnIndex(ThingManagerDBModel.ID)));//這裏進行id值的提取

異常:

  java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow.  Make sure the Cursor is initialized correctly before accessing data from it.
        at android.database.CursorWindow.nativeGetLong(Native Method)
        at android.database.CursorWindow.getLong(CursorWindow.java:513)
        at android.database.CursorWindow.getInt(CursorWindow.java:580)
        at android.database.AbstractWindowedCursor.getInt(AbstractWindowedCursor.java:69)
        at com.aserbao.aserbaosandroid.functions.database.mySql.beans.ThingDBController.queryRawById(ThingDBController.java:109)
        at com.aserbao.aserbaosandroid.functions.database.mySql.MySqlActivity.queryData(MySqlActivity.java:70)
        at com.aserbao.aserbaosandroid.functions.database.base.DataBaseBaseActivity.onViewClicked(DataBaseBaseActivity.java:85)
        at com.aserbao.aserbaosandroid.functions.database.base.DataBaseBaseActivity_ViewBinding$2.doClick(DataBaseBaseActivity_ViewBinding.java:48)
        at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)
        at android.view.View.performClick(View.java:6291)
        at android.view.View$PerformClick.run(View.java:24931)
        at android.os.Handler.handleCallback(Handler.java:808)
        at android.os.Handler.dispatchMessage(Handler.java:101)
        at android.os.Looper.loop(Looper.java:166)
        at android.app.ActivityThread.main(ActivityThread.java:7425)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)

參考鏈接

  1. Android 開發中使用 SQLite 數據庫
  2. Android數據庫Sqlite的基本用法及升級策略
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章