• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • Android學習筆記三十四之數據存儲—SQLite數據庫

     前面我們介紹了Android數據存儲的兩種方法:文件存儲和SharedPreference存儲,這一篇我們來學習一下android存儲數據的另外一種方式——SQLite數據庫存儲。

    1、SQlite數據庫簡介

      現在的主流移動智能設備中,比如Android手機、iPhone手機,平板等都是使用SQLite數據庫作為存儲復雜數據的存儲引擎。那么什么是SQLite數據庫呢?

      SQLite是D.Richard Hipp用C語言編寫的開源嵌入式數據庫引擎,它支持大多數的SQL92標準,并且可以在所有主要的操作系統上運行。SQLite由以下幾個部分組成:SQL編譯器、內核、后端以及附件。SQLite通過利用虛擬機和虛擬數據庫引擎(VDBE),是調試、修改和擴展SQLite的內核變得更加方便。所有SQL語句都被編譯成易讀的、可以在SQLite虛擬機中執行的程序集。

      SQlite數據庫是一個輕量級的關系型數據庫,不需要像其它關系型數據庫一樣需要安裝,Android已經將SQLite數據庫內置在系統中,內置的版本是3.0版本。SQlite支持標準的SQL語法,還支持ACID(數據庫事務)原則,占用資源非常少,非常適合在移動設備中使用。

      袖珍型的SQLite數據庫就可以支持高達2TB大小的數據庫,每個數據庫都是以單個文件的形式存在,這些數據都是以B-Tree的數據結構形式存儲在磁盤上。每一個數據庫是一個文件,數據庫中可以包含多個表,表中可以包含多個字段。

      SQLite數據庫支持NULL、INTEGER、REAL、TEXT和BLOB數據類型,分別代表空值、整型值、浮點值、字符串文本、二進制對象。SQLite采用動態數據類型,當某個值插入到數據庫時,SQLite將會檢查它的類型,如果該類型與關聯的列不匹配,SQLite則會嘗試將該值轉換成該列的類型,如果不能轉換,則該值將作為本身的類型存儲,SQLite稱這為“弱類型”。但有一個特例,如果是INTEGER PRIMARY KEY,則其他類型不會被轉換,會報一個“datatype missmatch”的錯誤。簡單的說就是:我們可以各種數據類型的數據保存到任何字段中而不用關心字段聲明的數據類型。

    下面是Android系統中SQLite數據庫的幾個關鍵類:

    • SQLiteOpenHelper:數據庫抽象類,我們通過繼承該類,獲取數據庫實例,然后可以重寫數據庫創建、更新版本方法和關閉數據庫

    • SQLiteDatabase:數據庫訪問類:我們通過獲取這個類的實例來對數據庫做一些CRUD操作

    • Cursor:游標:類似于JDBC里的結果集

    2、SQLiteOpenHelper創建數據庫與版本管理

      我們繼承SQLiteOpenHelper這個類的時候必須要實現兩個方法: onCreate(SQLiteDatabase sqLiteDatabase)和onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1),onCreate方法是我們第一次使用應用的時候生成數據表;onUpgrade方法是數據庫版本發生變更的時候調用。還有必須要實現一個構造方法,下面是代碼示例:

    public class DBOpenHelper extends SQLiteOpenHelper {
    private static String DB_NAME = "db_test.db";
    private static int DB_VERSION = 1;
    
    public DBOpenHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }
    
    /**
     * 數據庫第一次創建的時候調用
     *
     * @param sqLiteDatabase
     */
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
    
    }
    
    /**
     * 數據庫版本發生改變的時候調用
     *
     * @param sqLiteDatabase
     * @param i
     * @param i1
     */
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    
    }
    }
    

      上述代碼啟動會創建一個db_test.db的數據庫文件,在data/data/包名/database目錄下就可以看到創建的數據庫文件,將其導出之后就可以用圖形化工具查看。

    3、SQLite數據庫圖形化工具

      SQLite圖形化工具很多,推薦使用SQLite expert,這是官網下載地址。這個圖形化工具使用也很簡單,直接將從Android Device Monitor導出的數據庫文件導入就可以查看數據。這個可視化工具實用不是很復雜,就不在做很多的介紹了。

    4、SQLite數據庫使用

      下面我們進行數據庫的基本使用,在Android開發中,我們不需要寫很多SQL語句,Android已經幫我們封裝好了,我們直接調用就可以了。Android提供的CRUD操作的方法介紹:

    數據增加:
    insert(String table, String nullColumnHack, ContentValues values)
    

    參數解析:第一個是數據表名;第二個是設置為null值的數據表名;第三個是插入的數據,用ContentValues封裝。

    數據刪除:
    delete(String table, String whereClause, String[] whereArgs)
    

    參數解析:第一個是數據表名;第二個是where條件;第三個是約束

    數據更新:
    update(String table, ContentValues values, String whereClause, String[] whereArgs)
    

    參數解析:第一個是數據表名;第二個是改變的數據;第三個是where條件;第四個是約束

    數據查詢:
    query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
    
    query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit, CancellationSignal cancellationSignal)
    
    query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
    
    query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
    

    參數解析:

    table:表名稱
    
    colums:表示要查詢的列所有名稱集
    
    selection:表示WHERE之后的條件語句,可以使用占位符
    
    selectionArgs:條件語句的參數數組
    
    groupBy:指定分組的列名
    
    having:指定分組條件,配合groupBy使用
    
    orderBy:y指定排序的列名
    
    limit:指定分頁參數
    
    distinct:指定“true”或“false”表示要不要過濾重復值
    
    Cursor:返回值,相當于結果集ResultSet
    

    下面通過實現數據庫的基本CRUD操作體會一下:

    Activity代碼:

    package com.example.datasave;
    
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by Devin on 2016/7/20.
     */
    public class SQLiteActivity extends AppCompatActivity {
    private Button btn_sqlite_insert;
    private Button btn_sqlite_delete;
    private Button btn_sqlite_update;
    private Button btn_sqlite_select;
    private SQLiteDatabase mDatabase;
    private DBOpenHelper mDBOpenHelper;
    private static final String TAG = "SQLiteActivity";
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sqlite);
    
        mDBOpenHelper = new DBOpenHelper(this);
        mDatabase = mDBOpenHelper.getWritableDatabase();
        btn_sqlite_insert = (Button) findViewById(R.id.btn_sqlite_insert);
        btn_sqlite_insert.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                insert();
            }
        });
        btn_sqlite_delete = (Button) findViewById(R.id.btn_sqlite_delete);
        btn_sqlite_delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                delete(5);
            }
        });
        btn_sqlite_update = (Button) findViewById(R.id.btn_sqlite_update);
        btn_sqlite_update.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                update(6);
            }
        });
        btn_sqlite_select = (Button) findViewById(R.id.btn_sqlite_select);
        btn_sqlite_select.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                select();
            }
        });
    }
    
    private void select() {
        Cursor cursor = mDatabase.query("student", null, null, null, null, null, null);
        List<StudentModel> students = new ArrayList<>();
        while (cursor.moveToNext()) {
            StudentModel model = new StudentModel();
            model.setS_id(cursor.getInt(cursor.getColumnIndex("s_id")));
            model.setName(cursor.getString(cursor.getColumnIndex("name")));
            model.setSex(cursor.getString(cursor.getColumnIndex("sex")));
            model.setGrade(cursor.getString(cursor.getColumnIndex("grade")));
            students.add(model);
            Log.i(TAG, "數據---->>" + model.toString());
        }
        cursor.close();
        ToastUtils.showToast(this, students.toString());
    }
    
    private void update(int s_id) {
        ContentValues values = new ContentValues();
        values.put("grade", "2013級信計班");
        mDatabase.update("student", values, "s_id=?", new String[]{"" + s_id + ""});
        ToastUtils.showToast(this, "更新了s_id為" + s_id + "的數據");
    }
    
    
    private void insert() {
        ContentValues values = new ContentValues();
        values.put("name", "李四");
        values.put("sex", "男");
        values.put("grade", "2012級數應班");
        mDatabase.insert("student", null, values);
        ToastUtils.showToast(this, "插入數據成功");
    }
    
    private void delete(int position) {
        mDatabase.delete("student", "s_id=?", new String[]{"" + position + ""});
        ToastUtils.showToast(this, "刪除了s_id為" + position + "的數據");
    }
    }
    

    SQLiteOpenHelper代碼:

    package com.example.datasave;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    /**
     * Created by Devin on 2016/7/20.
     */
    public class DBOpenHelper extends SQLiteOpenHelper {
    private static String DB_NAME = "db_test.db";
    private static int DB_VERSION = 1;
    
    
    private static final String TEST_TABLE = "CREATE TABLE student (s_id INTEGER PRIMARY KEY AUTOINCREMENT," +
            "name VARCHAR(20),sex VARCHAR(10),grade VARCHAR(100))";
    
    public DBOpenHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }
    
    /**
     * 數據庫第一次創建的時候調用
     *
     * @param sqLiteDatabase
     */
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(TEST_TABLE);
    }
    
    /**
     * 數據庫版本發生改變的時候調用
     *
     * @param sqLiteDatabase
     * @param i
     * @param i1
     */
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    
    }
    }
    

    布局文件非常簡單,就不再貼代碼了,下面是實現效果圖:

    由于是錄制GIF,速度比較慢,這樣就可以基本實現數據庫的CRUD操作。

    5、SQLite數據庫事務

      上面我們介紹了SQLite的基本操作,現在我們來介紹一下SQLite數據庫的事務,首先,來了解一下什么是事務?

    什么是事務?

      數據事務是指作為單個邏輯工作單元執行的一系列操作,要么完全地執行,要么完全地不執行。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性。簡單的說,就是多個操作捆綁到一起執行,只有所有的操作都成功了,事務才算執行完畢,才會提交,只要有一個操作沒有完成,事務都會回滾,所有的操作都不回提交。舉一個例子就是:張三向李四轉賬1000塊,張三的賬戶上要減少1000塊,李四的賬戶上要增加1000塊,只有兩個操作都完成,事務才會提交。假如張三的賬戶減少了1000塊,但是李四的賬戶上沒有增加1000塊;或者張三的賬戶沒有減少1000塊,但是李四的賬戶增加了1000塊,這顯然都是不可以的。

    比如下面的例子代碼:

    /**
     * 事務測試
     */
    public void payment() {
        SQLiteDatabase db = mDBOpenHelper.getWritableDatabase();
        //開啟事務 
        db.beginTransaction();
        try {
            db.execSQL("update person set amount=amount-10 where personid=?", new Object[]{1});
            db.execSQL("update person set amount=amount+10 where personid=?", new Object[]{2});
            //設置事務標志為成功,當結束事務時就會提交事務 
            db.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //結束事務 
            db.endTransaction();
        }
    }
    

    簡單的說就是:只有寫在事務里的所有數據庫操作都成功,事務才會提交,否則,事務回滾回原來的狀態。在data/data/<包名>/database/目錄下,我們創建數據庫的時候也會創建一個xxx.db-journal讓數據庫支持事務的臨時日志文件。

    6、SQLite數據庫二進制大文件保存和讀取

      一般情況下,我們很少在數據庫里面存儲大文件,視頻、音頻、圖片等我們都是保存路徑,但是有些特殊情況下,我們需要將大文件保存到數據庫里面,下面我們就實現一個簡單的例子:

    SQLiteDatabase db = mDBService.getWritableDatabase(); // 得到數據庫
    try {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ((BitmapDrawable) iv.getDrawable()).getBitmap().compress(
    CompressFormat.PNG, 100, baos);//壓縮為PNG格式,100表示跟原圖大小一樣
    Object[] args = new Object[] {baos.toByteArray() };
    db.execSQL(INSERT_SQL, args);
    baos.close();
    db.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    
    //3.從數據庫中取圖片
    public void getPhoto() {
    String SELECT_SQL = "SELECT photo FROM launcher";
    ImageView appIcon = (ImageView) v.findViewById(R.id.appicon);//v是我在類中定義的一個view對象,跟前面保存圖片一樣
    byte[] photo = null;
    mDBService = new DBService(getContext());
    SQLiteDatabase db = mDBService.getReadableDatabase();
    Cursor mCursor = db.rawQuery(SELECT_SQL, null);
    if (mCursor != null) {
    if (mCursor.moveToFirst()) {//just need to query one time
    photo = mCursor.getBlob(mCursor.getColumnIndex("photo"));//取出圖片
    }
    }
    if (mCursor != null) {
    mCursor.close();
    }
    db.close();
    ByteArrayInputStream bais = null;
    if (photo != null) {
    bais = new ByteArrayInputStream(photo);
    appIcon.setImageDrawable(Drawable.createFromStream(bais, "photo"));//把圖片設置到ImageView對象中
    } 
    //appIcon顯示的就是之前保存到數據庫中的圖片
    }
    

    7、SimpleCursorAdapter綁定數據庫數據

      這種比較少用,我們就實現一個簡單的例子:

    Map<String, String> map = new HashMap<String, String>();
    
        ListView listView = (ListView) this.findViewById(R.id.list);
        Cursor cursor = getContentResolver().query(
                ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
        if(cursor != null){
    
          startManagingCursor(cursor);
    
       }       
        ListAdapter adapter = new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_1, cursor,
                new String[] { PhoneLookup.DISPLAY_NAME },
                new int[] { android.R.id.text1 });
        listView.setAdapter(adapter);
        stopManagingCursor();
    

    SQLite數據庫就簡單介紹到這里,網絡上有很多好的數據庫操作框架,后面再介紹。

    版權聲明:本文為u012426327原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/u012426327/article/details/77834269

    智能推薦

    Android使用已有sqlite數據庫——內部存儲

    ——熱愛開源,樂于分享 Android使用已有sqlite數據庫——內部存儲 上一篇介紹了如何在手機sd卡上使用已有的sqlite數據庫,但是如果手機沒有sd卡這個外部存儲,則怎么實現呢?今天講述使用Android手機內部存儲使用已有的sqlite數據庫。 一、拷貝數據庫文件到當前工程 拷貝到res/raw下: 二、代碼: 這是我在利用Android...

    17讀書筆記之SQLite數據庫存儲

    SQLite數據庫存儲 創建數據庫 Android管理數據庫提供了一個SQLiteOpenHelper幫助類。 借助這個類就可以簡單地對數據庫進行創建和升級。下面學習SQLiteOpenHelper的基本用法。 首先SQLiteOpenHelper是一個抽象類,我們要是用的話,需要創建一個自己的幫助類去繼承它。SQLiteOpenHelper中有兩個抽象方法。 onCreate()和onUpgra...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    猜你喜歡

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    styled-components —— React 中的 CSS 最佳實踐

    https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    精品国产乱码久久久久久蜜桃不卡