6.3 數(shù)據(jù)儲(chǔ)存-SQLite數(shù)據(jù)庫(kù)一

標(biāo)注:本文為個(gè)人學(xué)習(xí)使用,僅做自己學(xué)習(xí)參考使用,請(qǐng)勿轉(zhuǎn)載和轉(zhuǎn)發(fā)
2018-08-13: 初稿,最近不開心,不開心,參考博主coder-pig

0. 引言

  • 主要學(xué)習(xí)關(guān)于android的數(shù)據(jù)儲(chǔ)存的第三種訪問方式:Sqlite數(shù)據(jù)庫(kù),該數(shù)據(jù)庫(kù)和其他的數(shù)據(jù)庫(kù)不同,不需要在手機(jī)上安裝數(shù)據(jù)庫(kù)軟件,android已經(jīng)集成了這個(gè)數(shù)據(jù)庫(kù),不需要其他的數(shù)據(jù)庫(kù)軟件安裝,完成相關(guān)配置。

1. 基本概念

1.1 Sqlite基本介紹

1)Sqlite是一個(gè)輕量級(jí)的關(guān)系型的數(shù)據(jù)庫(kù),運(yùn)算速度快,占用資源少,很適合在移動(dòng)設(shè)備上使用,不僅支持SQL語法,還遵循ACID(數(shù)據(jù)庫(kù)事務(wù))的原則,無需賬號(hào),使用起來非常方便!
2)前面學(xué)習(xí)了使用文件SharedPerference來保存數(shù)據(jù),在很多情況下,文件并不一定是有效的,如付哦線程并發(fā)訪問是相關(guān)的,app要處理可能變化的復(fù)雜數(shù)據(jù)結(jié)構(gòu)等等,比如銀行的存錢和曲線,使用前兩則和會(huì)顯得很無力或者繁瑣,但數(shù)據(jù)庫(kù)可以解決這個(gè)問題,android還提供了一個(gè)原聲的sqlite
3)sqlite支持了五種數(shù)據(jù)類型,NULL、INTEGER、REAL(浮點(diǎn)數(shù))、TEXT(字符串文本)和BLOB(二進(jìn)制對(duì)象),雖然只是又5中,但是對(duì)于varchar、char等其他的數(shù)據(jù)類型都是可以保存的,因?yàn)镾qlite有個(gè)最大的特點(diǎn),你可以各種數(shù)據(jù)類型的數(shù)據(jù)保存到任何字段中而不用管子你字段聲明的數(shù)據(jù)類型是什么,撥入你可以在interger類型中的字段中存放字符串,當(dāng)然除了聲明為主鍵INTEGER PRIMARY的字段只能夠儲(chǔ)存64位整數(shù)!
4)另外sqlite在解析CREATE TABLE語句的時(shí)候,會(huì)忽略CREATE TABLE語句跟在字段后面的數(shù)據(jù)類型信息,如下面語句會(huì)忽略name字段的類型信息:CRATE TABLE person (personid integer primary key autoincrement, name varchar(20))
5)android內(nèi)置的Sqlite是Sqlite3版本的

1.2 幾個(gè)相關(guān)的類
  • 數(shù)據(jù)庫(kù)使用的時(shí)候用到的三個(gè)類
  1. SQLiteOpenHelper:抽象類,我們通過集成該類,然后重寫數(shù)據(jù)庫(kù)創(chuàng)建以及更新的方法,我們還可以通過該類的對(duì)象獲得數(shù)據(jù)庫(kù)實(shí)例,或者關(guān)閉數(shù)據(jù)庫(kù)!
  2. SQLiteDatabase:數(shù)據(jù)庫(kù)訪問類,我們可以通過該類的對(duì)象來對(duì)數(shù)據(jù)庫(kù)做一些增刪改查的操作
  3. Cursor: 游標(biāo),有點(diǎn)類似于JDBC里的resultset,結(jié)果集!可以簡(jiǎn)單理解為指向數(shù)據(jù)庫(kù)中的某一個(gè)記錄的指針!

2. 使用SQLiteOpenHelper類創(chuàng)建數(shù)據(jù)庫(kù)與版本管理

  • 對(duì)于設(shè)計(jì)數(shù)據(jù)庫(kù)的app,我們不可能手動(dòng)地去給他創(chuàng)建數(shù)據(jù)庫(kù)文件,所以需要在第一次啟動(dòng)app的時(shí)候就創(chuàng)建好數(shù)據(jù)庫(kù)表;
  • 而當(dāng)我們的應(yīng)用進(jìn)行升級(jí)需要修改數(shù)據(jù)庫(kù)表的結(jié)構(gòu)時(shí),這個(gè)時(shí)候就需要對(duì)數(shù)據(jù)庫(kù)表進(jìn)行更新了;對(duì)于這兩個(gè)操作,android給外面提供另外SQLiteOpenHelper的兩個(gè)方法,onCreate()與onUpgrade()來實(shí)現(xiàn)

方法解析:

  • onCreate(datebase): 首次使用軟件時(shí)生成數(shù)據(jù)庫(kù)表
  • onUpgrade(database, oldVersion, newVersion): 在數(shù)據(jù)庫(kù)的版本發(fā)生變化時(shí)會(huì)被調(diào)用,一般是軟件升級(jí)時(shí)才需要改變版本號(hào),而數(shù)據(jù)庫(kù)的版本是由程序員控制的,假設(shè)數(shù)據(jù)庫(kù)的現(xiàn)在的版本是1,由于業(yè)務(wù)的變更,修改了數(shù)據(jù)庫(kù)表結(jié)構(gòu),這個(gè)時(shí)候就需要升級(jí)軟件,升級(jí)軟件時(shí)希望更新用戶手機(jī)里的數(shù)據(jù)庫(kù)的表的結(jié)構(gòu),為了實(shí)現(xiàn)這一目的,可以吧原來的數(shù)據(jù)庫(kù)斑斑設(shè)置為2或者其他與舊版本號(hào)不同的數(shù)字即可!

代碼示例:

public class MyDBOpenHelper extends SQLiteOpenHelper {
    public MyDBOpenHelper(Context context, String name, CursorFactory factory,
            int version) {super(context, "my.db", null, 1); }
    @Override
    //數(shù)據(jù)庫(kù)第一次創(chuàng)建時(shí)被調(diào)用
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE person(personid INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20))");
        
    }
    //軟件版本號(hào)發(fā)生改變時(shí)調(diào)用
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("ALTER TABLE person ADD phone VARCHAR(12) NULL");
    }
}

代碼解析:

  • 上述代碼第一次啟動(dòng)應(yīng)用,我們會(huì)創(chuàng)建這個(gè)my.db的文件,并且會(huì)執(zhí)行onCreate()里的方法, 創(chuàng)建一個(gè)Person的表,他又兩個(gè)字段,主鍵personId和name字段;接著如我我們修改db的版本 號(hào),那么下次啟動(dòng)就會(huì)調(diào)用onUpgrade()里的方法,往表中再插入一個(gè)字段!另外這里是插入 一個(gè)字段,所以數(shù)據(jù)不會(huì)丟失,如果是重建表的話,表中的數(shù)據(jù)會(huì)全部丟失,下一節(jié)我們會(huì) 來教大家如何解決這個(gè)問題!

流程小結(jié):

  1. 自定義一個(gè)類集成SQLiteOpenHelper類
  2. 在該類的構(gòu)造方法的super中設(shè)置好要?jiǎng)?chuàng)建的數(shù)據(jù)庫(kù)名、版本號(hào)
  3. 重寫onCreate()方法創(chuàng)建表結(jié)構(gòu)
  4. 重寫onUpgrade()方法定義版本號(hào)發(fā)生改變后執(zhí)行的操作

3. 如何查看我們生成的db文件

  • 當(dāng)我們調(diào)用上面的MyDbOpenHelper的對(duì)象的getWriteableDatabase()就會(huì)在下述目錄下創(chuàng)建我們的db數(shù)據(jù)庫(kù)文件:



    我們發(fā)現(xiàn)數(shù)據(jù)庫(kù)又兩個(gè),前者是我們創(chuàng)建的數(shù)據(jù)庫(kù),而后者是為了能讓數(shù)據(jù)庫(kù)支持事務(wù)而產(chǎn)生的臨時(shí)日志文件,一般的大小都是0字節(jié)!而在File Explorer里我們確實(shí)是打不開文件的,連txt文件都打不開,何況是.db,所以下面有兩種選擇方式

  1. 先導(dǎo)出來,然后用SQLite的圖形化工具查看
  2. 配置adb環(huán)境變量后,通過adb shell 來查看

其中adb的查看方式,當(dāng)前的設(shè)備最好可以root,這樣查看database比較方便

// 第一步
adb shell
// 第二步, root之后可以對(duì)文件夾進(jìn)行聯(lián)想
su
// 第三步
cd data/data/包名/databases
// 第四步,打開數(shù)據(jù)庫(kù)文件
sqlite3 my.db
// 第五步,查看數(shù)據(jù)庫(kù)中的表
.table
// 然后就可以對(duì)數(shù)據(jù)庫(kù)中的表格進(jìn)行操作了

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

  • android還提供了一些關(guān)于數(shù)據(jù)庫(kù)的相關(guān)語法,也就是API相關(guān)方法的使用
    代碼示例

運(yùn)行效果圖

實(shí)現(xiàn)代碼

布局過于簡(jiǎn)單,就四個(gè)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++;
                //參數(shù)依次是:表名,強(qiáng)行插入null值得數(shù)據(jù)列的列名,一行記錄的數(shù)據(jù)
                db.insert("person", null, values1);
                Toast.makeText(mContext, "插入完畢~", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn_query:
                sb = new StringBuilder();
                //參數(shù)依次是:表名,列名,where約束條件,where中占位符提供具體的值,指定group by的列,進(jìn)一步約束
                //指定查詢結(jié)果的排序方式
                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", "嘻嘻~");
                //參數(shù)依次是表名,修改后的值,where條件,以及約束,如果不指定三四兩個(gè)參數(shù),會(huì)更改所有行
                db.update("person", values2, "name = ?", new String[]{"呵呵~2"});
                break;
            case R.id.btn_delete:
                //參數(shù)依次是表名,以及where條件與約束
                db.delete("person", "personid = ?", new String[]{"3"});
                break;
        }
    }
}

5.使用SQL語句操作數(shù)據(jù)庫(kù)

  • 當(dāng)然,你可可能學(xué)習(xí)過SQL語句,但不想使用android的這些API,也可以直接使用SQLiteDatabase給我們提供的相關(guān)方法:
  • execSQL(SQL, Object[]): 使用帶占位符的SQL語句,這個(gè)是執(zhí)行修改數(shù)據(jù)庫(kù)內(nèi)容的sql語句用的
  • rawQuery(SQL, Object[]): 使用帶占位符的SQL查詢操作,另外前面忘了介紹下Cursor這個(gè)東西以及相關(guān)屬性,這里補(bǔ)充下:
    Cursor對(duì)象有點(diǎn)類似于JDBC倆面的resultSet,結(jié)果集!使用差不多,提供一下方法移動(dòng)查詢結(jié)果的記錄指針;
  • move(offset): 指定向上或者向下移動(dòng)的行數(shù),整數(shù)表示向下移動(dòng),負(fù)數(shù)表示向上移動(dòng)!
  • moveToFirst(): 指針移動(dòng)到第一行,成功返回true,也說明有數(shù)據(jù)
  • moveToLast(): 指針移動(dòng)到最后一樣,成功返回true;
  • moveToNext(): 指針移動(dòng)到下一行,成功返回true,表明還有元素!
  • moveToPrevious(): 移動(dòng)到上一條記錄
  • getCount(): 獲得總得數(shù)據(jù)條數(shù)
  • isFirst(): 是否為第一條記錄
  • isLast(): 是否為最后一項(xiàng)
  • moveToPosition(int): 移動(dòng)到指定行
  1. 插入數(shù)據(jù)
public void save(Person p)
{
    SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
    db.execSQL("INSERT INTO person(name,phone) values(?,?)",
                new String[]{p.getName(),p.getPhone()});
}
  1. 刪除數(shù)據(jù)
public void delete(Integer id)
{
    SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
    db.execSQL("DELETE FROM person WHERE personid = ?",
                new String[]{id});
}
  1. 修改數(shù)據(jù)
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()});
}
  1. 查詢數(shù)據(jù)
public Person find(Integer id)
{
    SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
    Cursor cursor =  db.rawQuery("SELECT * FROM person WHERE personid = ?",
            new String[]{id.toString()});
    //存在數(shù)據(jù)才返回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;
}
  1. 數(shù)據(jù)分頁(yè)
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;
}
  1. 查詢記錄數(shù)
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;      
}   
  • 除了上面獲取條數(shù)的方法外還可以使用cursor.getCount()方法獲得數(shù)據(jù)的條數(shù), 但是SQL語句要改改!比如SELECT * FROM person;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,036評(píng)論 25 709
  • 轉(zhuǎn) # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    呂品?閱讀 10,123評(píng)論 0 44
  • 請(qǐng)問如何在屏幕左側(cè)顯示目錄索引?答:視圖——顯示——勾選導(dǎo)航窗格 請(qǐng)問如何調(diào)出紙張最上方的標(biāo)尺工具答:視圖——顯示...
    默寫年華Antifragile閱讀 138評(píng)論 0 0
  • 放松 髂腰肌放松 翻書,畫圓,靠墻手上舉 動(dòng)作一,器械推胸 收腹,沉肩,胸挺出來一點(diǎn),大臂略低于肩膀,小臂垂直向前...
    搖月亮閱讀 348評(píng)論 0 0
  • 喧囂的城市里,忙碌了一天的你,過的 還好嗎?是不是很久都沒有認(rèn)真的去讀過一篇文章了? . 如果你活到88歲,...
    悅落閱讀 251評(píng)論 0 1

友情鏈接更多精彩內(nèi)容