4.2.1 SQLite數(shù)據(jù)庫的創(chuàng)建和升級

本節(jié)例程下載地址:WillFlowDatabast

一敬飒、SQLite簡介

SQLite 是一款輕量級的關(guān)系型數(shù)據(jù)庫邪铲,它的運算速度非常快无拗,占用資源很少带到,通常只需要幾百 K 的內(nèi)存就足夠了,因而特別適合在移動設(shè)備上使用英染。SQLite 不僅支持標準的 SQL 語法揽惹,還遵循了數(shù)據(jù)庫的 ACID 事務(wù)被饿,所以只要你以前使用過其他的關(guān)系型數(shù)據(jù)庫,就可以很快地上手 SQLite搪搏。而 SQLite 又比一般的數(shù)據(jù)庫要簡單得多狭握,它甚至不用設(shè)置用戶名和密碼就可以使用。 Android 正是把這個功能極為強大的數(shù)據(jù)庫嵌入到了系統(tǒng)當中慕嚷,使得本地持久化的功能有了一次質(zhì)的飛躍哥牍。

前面我們所學(xué)的文件存儲和 SharedPreferences 存儲畢竟只適用于去保存一些簡單的數(shù)據(jù)和鍵值對,當需要存儲大量復(fù)雜的關(guān)系型數(shù)據(jù)的時候喝检,你就會發(fā)現(xiàn)以上兩種存儲方式很難應(yīng)付得了嗅辣。比如我們手機的短信程序中可能會有很多個會話,每個會話中又包含了很多條信息內(nèi)容挠说,并且大部分會話還可能各自對應(yīng)了電話簿中的某個聯(lián)系人澡谭。很難想象如何用文件或者 SharedPreferences 來存儲這些數(shù)據(jù)量大、結(jié)構(gòu)性復(fù)雜的數(shù)據(jù)吧损俭?但是使用數(shù)據(jù)庫就可以做得到蛙奖。那么我們就趕快來看一看, Android 中的 SQLite 數(shù)據(jù)庫到底是如何使用的杆兵。

二雁仲、創(chuàng)建數(shù)據(jù)庫

Android 為了讓我們能夠更加方便地管理數(shù)據(jù)庫,專門提供了一個 SQLiteOpenHelper 幫助類琐脏,借助這個類就可以非常簡單地對數(shù)據(jù)庫進行創(chuàng)建和升級攒砖。

首先我們要知道 SQLiteOpenHelper 是一個抽象類,這意味著如果我們想要使用它的話日裙,就需要創(chuàng)建一個自己的幫助類去繼承它吹艇。 SQLiteOpenHelper 中有兩個抽象方法,分別是 onCreate() 和 onUpgrade()昂拂,我們必須在自己的幫助類里面重寫這兩個方法受神,然后分別在這兩個方法中去實現(xiàn)創(chuàng)建、升級數(shù)據(jù)庫的邏輯格侯。

SQLiteOpenHelper 中還有兩個非常重要的實例方法鼻听,getReadableDatabase() 和 getWritableDatabase()。這兩個方法都可以創(chuàng)建或打開一個現(xiàn)有的數(shù)據(jù)庫(如果數(shù)據(jù)庫已存在則直接打開联四,否則創(chuàng)建一個新的數(shù)據(jù)庫)撑碴,并返回一個可對數(shù)據(jù)庫進行讀寫操作的對象。不同的是碎连,當數(shù)據(jù)庫不可寫入的時候(如磁盤空間已滿) getReadableDatabase()方法返回的對象將以只讀的方式去打開數(shù)據(jù)庫灰羽,而 getWritableDatabase()方法則將出現(xiàn)異常驮履。

SQLiteOpenHelper 中有兩個構(gòu)造方法可供重寫鱼辙,一般使用參數(shù)少一點的那個構(gòu)造方法即可廉嚼。這個構(gòu)造方法中接收四個參數(shù):
第一個參數(shù)是 Context,這個沒什么好說的倒戏,必須要有它才能對數(shù)據(jù)庫進行操作怠噪。
第二個參數(shù)是數(shù)據(jù)庫名,創(chuàng)建數(shù)據(jù)庫時使用的就是這里指定的名稱杜跷。
第三個參數(shù)允許我們在查詢數(shù)據(jù)的時候返回一個自定義的 Cursor傍念,一般都是傳入 null。
第四個參數(shù)表示當前數(shù)據(jù)庫的版本號葛闷,可用于對數(shù)據(jù)庫進行升級操作憋槐。

構(gòu)建出 SQLiteOpenHelper 的實例之后,再調(diào)用它的 getReadableDatabase() 或 getWritableDatabase()方法就能夠創(chuàng)建數(shù)據(jù)庫了淑趾,數(shù)據(jù)庫文件會存放在/data/data/<package name>/databases/目錄下阳仔。此時,重寫的 onCreate() 方法也會得到執(zhí)行扣泊,所以通常會在這里去處理一些創(chuàng)建表的邏輯近范。接下來還是讓我們通過例子的方式來更加直觀地體會 SQLiteOpenHelper 的用法吧。

這里我們希望創(chuàng)建一個名為 BookStore.db 的數(shù)據(jù)庫延蟹,然后在這個數(shù)據(jù)庫中新建一張 Book 表评矩,表中有 id( 主鍵)、作者阱飘、價格斥杜、 頁數(shù)和書名等列。創(chuàng)建數(shù)據(jù)庫表當然還是需要用建表語句的俯萌,這里也是要考驗一下我們的 SQL 基本功了果录,Book 表的建表語句如下所示:

    create table Book (
            id integer primary key autoincrement,
            author text,
            price real,
            pages integer,
            name text)

只要你對 SQL 方面的知識稍微有一些了解,上面的建表語句對你來說應(yīng)該都不難咐熙。SQLite 不像其他的數(shù)據(jù)庫擁有眾多繁雜的數(shù)據(jù)類型弱恒,它的數(shù)據(jù)類型很簡單:integer 表示整型,real 表示浮點型棋恼, text 表示文本類型返弹, blob 表示二進制類型。 另外爪飘,上述建表語句中我們還使用了 primary key 將 id 列設(shè)為主鍵义起,并用 autoincrement 關(guān)鍵字表示 id 列是自增長的。然后需要在代碼中去執(zhí)行這條 SQL語句师崎,才能完成創(chuàng)建表的操作默终。

新建 MyDatabaseHelper,代碼如下:
/**
 * Created by   : WGH.
 */
public class MyDatabaseHelper extends SQLiteOpenHelper {
    private Context mContext;

    public static final String CREATE_BOOK = "create table book ("
            + "id integer primary key autoincrement, "
            + "author text, "
            + "price real, "
            + "pages integer, "
            + "name text)";

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        this.mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(CREATE_BOOK);
        Toast.makeText(mContext, "創(chuàng)建數(shù)據(jù)庫成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}

我們把建表語句定義成了一個字符串常量,然后在 onCreate() 方法中又調(diào)用了 SQLiteDatabase 的 execSQL() 方法去執(zhí)行這條建表語句齐蔽,并彈出一個 Toast 提示創(chuàng)建成功两疚,這樣就可以保證在數(shù)據(jù)庫創(chuàng)建完成的同時還能成功創(chuàng)建 Book 表。

修改 activity_main.xml 中的代碼含滴,如下所示:
    <Button
        android:id="@+id/buttonDataBase"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="創(chuàng)建數(shù)據(jù)庫"
        android:textColor="#0babf5"
        android:textSize="25dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
最后修改 MainActivity 中的代碼诱渤,如下所示:
public class MainActivity extends AppCompatActivity {
    private MyDatabaseHelper myDatabaseHelper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.buttonDataBase);
        myDatabaseHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myDatabaseHelper.getWritableDatabase();
            }
        });
    }
}

這里我們在 onCreate() 方法中構(gòu)建了一個 MyDatabaseHelper 對象,并且通過構(gòu)造函數(shù)的參數(shù)將數(shù)據(jù)庫名指定為 BookStore.db谈况,版本號指定為 1勺美,然后在 Create database 按鈕的點擊事件里調(diào)用了 getWritableDatabase() 方法。這樣當?shù)谝淮吸c擊 Create database 按鈕時碑韵,就會檢測到當前程序中并沒有 BookStore.db 這個數(shù)據(jù)庫赡茸,于是會創(chuàng)建該數(shù)據(jù)庫并調(diào)用 MyDatabaseHelper 中的 onCreate() 方法,這樣 Book 表也就得到了創(chuàng)建祝闻,然后會彈出一個 Toast 提示創(chuàng)建成功坛掠。再次點擊 Create database 按鈕時,會發(fā)現(xiàn)此時已經(jīng)存在 BookStore.db 數(shù)據(jù)庫了治筒,因此不會再創(chuàng)建一次屉栓。

編譯運行看效果:

此時 BookStore.db 數(shù)據(jù)庫和 Book 表應(yīng)該都已經(jīng)創(chuàng)建成功了,因為當我們再次點擊按鈕時不會再有 Toast 彈出耸袜∮讯啵可是又回到了之前的那個老問題,怎樣才能證實它們的確是創(chuàng)建成功了堤框?如果還是使用 File Explorer域滥,那么最多你只能看到 databases 目錄下出現(xiàn)了一個 BookStore.db 文件, Book 表是無法通過 File Explorer 看到的蜈抓。因此這次我們準備換一種查看方式启绰,使用 adb shell 來對數(shù)據(jù)庫和表的創(chuàng)建情況進行檢查。

adb 是 Android SDK 中自帶的一個調(diào)試工具沟使, 使用這個工具可以直接對連接在電腦上的手機或模擬器進行調(diào)試操作委可。更詳細的介紹你可以參看這里:。腊嗡。着倾。

打開命令行界面,輸入 adb shell燕少,就會進入到設(shè)備的控制臺卡者,然后使用 cd 命令切換到 /data/data/com.example.databasetest/databases/ 目錄下, 并使用 ls 命令查看到該目錄里的文件:


這個目錄下出現(xiàn)了兩個數(shù)據(jù)庫文件客们,一個正是我們創(chuàng)建的 BookStore.db崇决,而另一個 BookStore.db-journal 則是為了讓數(shù)據(jù)庫能夠支持事務(wù)而產(chǎn)生的臨時日志文件材诽,通常情況下這個文件的大小都是 0 字節(jié)。

接下來我們就要借助 sqlite 命令來打開數(shù)據(jù)庫了恒傻,只需要鍵入 sqlite3岳守,后面加上數(shù)據(jù)庫名即可:


然后就已經(jīng)打開了 BookStore.db 數(shù)據(jù)庫,現(xiàn)在就可以對這個數(shù)據(jù)庫中的表進行管理了碌冶。首先來看一下目前數(shù)據(jù)庫中有哪些表,鍵入.table 命令:


可以看到此時數(shù)據(jù)庫中有兩張表涝缝,android_metadata 表是每個數(shù)據(jù)庫中都會自動生成的扑庞,而另外一張 Book 表就是我們在 MyDatabaseHelper 中創(chuàng)建的了。這里還可以通過 .schema 命令來查看它們的建表語句:


由此證明拒逮, BookStore.db 數(shù)據(jù)庫和 Book 表確實已經(jīng)是創(chuàng)建成功了罐氨。之后鍵入 .exit 或 .quit 命令可以退出數(shù)據(jù)庫的編輯,再鍵入 exit 命令就可以退出設(shè)備控制臺了滩援。

三栅隐、升級數(shù)據(jù)庫

如果你足夠細心,一定會發(fā)現(xiàn) MyDatabaseHelper 中還有一個 onUpgrade() 方法玩徊,它是用于對數(shù)據(jù)庫進行升級的租悄,它在整個數(shù)據(jù)庫的管理工作當中起著非常重要的作用。目前我們已經(jīng)有一張 Book 表用于存放書的各種詳細數(shù)據(jù)恩袱,如果我們想再添加一張 Category 表用于記錄書籍的分類比如: Category 表中有 id(主鍵)泣棋、分類名和分類代碼這幾個列,那么建表語句就可以寫成:

    create table Category (
            id integer primary key autoincrement,
            category_name text,
            category_code integer)
將這條建表語句添加到 MyDatabaseHelper 中畔塔,代碼如下:
    private static final String CREATE_BOOK = "create table book ("
            + "id integer primary key autoincrement, "
            + "author text, "
            + "price real, "
            + "pages integer, "
            + "name text)";

    private static final String CREATE_CATEGORY = "create table Category ("
            + "id integer primary key autoincrement, "
            + "category_name text, "
            + "category_code integer)";

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        this.mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(CREATE_BOOK);
        sqLiteDatabase.execSQL(CREATE_CATEGORY);
        Toast.makeText(mContext, "創(chuàng)建數(shù)據(jù)庫成功", Toast.LENGTH_SHORT).show();
    }

其實這個時候我們?nèi)c擊創(chuàng)建數(shù)據(jù)庫按鈕是不能成功的潭辈,因為此時 BookStore.db 數(shù)據(jù)庫已經(jīng)存在了,之后不管我們怎樣點擊創(chuàng)建數(shù)據(jù)庫按鈕澈吨, MyDatabaseHelper 中的 onCreate() 方法都不會再次執(zhí)行把敢,因此新添加的表也就無法得到創(chuàng)建了。所以我們需要運用 SQLiteOpenHelper 的升級功能解決這個問題谅辣。

修改 MyDatabaseHelper 中的代碼修赞,如下所示:
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        sqLiteDatabase.execSQL("drop table if exists Book");
        sqLiteDatabase.execSQL("drop table if exists Category");
        onCreate(sqLiteDatabase);
    }

可以看到,我們在 onUpgrade() 方法中執(zhí)行了兩條 DROP 語句桑阶,如果發(fā)現(xiàn)數(shù)據(jù)庫中已經(jīng)存在 Book 表或 Category 表了榔组,就將這兩張表刪除掉,然后再調(diào)用 onCreate() 方法去重新創(chuàng)建联逻。這里先將已經(jīng)存在的表刪除掉搓扯,是因為如果在創(chuàng)建表時發(fā)現(xiàn)這張表已經(jīng)存在了,就會直接報錯包归。

接下來我們讓 onUpgrade( )方法能夠執(zhí)行:看一下 SQLiteOpenHelper 的構(gòu)造方法里接收的第四個參數(shù)锨推,它表示當前數(shù)據(jù)庫的版本號,之前我們傳入的是 1,現(xiàn)在只要傳入一個比 1 大的數(shù)换可,就可以讓 onUpgrade() 方法得到執(zhí)行了椎椰。

修改 MainActivity 中的代碼,如下所示:
public class MainActivity extends AppCompatActivity {
    private MyDatabaseHelper myDatabaseHelper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        myDatabaseHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
        ...
    }
}

這里將數(shù)據(jù)庫版本號指定為 2沾鳄,表示我們對數(shù)據(jù)庫進行升級了】現(xiàn)在重新運行程序,并點擊按鈕译荞,這時就會再次彈出和剛才一樣的創(chuàng)建成功提示瓤的。為了驗證一下 Category 表是不是已經(jīng)創(chuàng)建成功了,我們在 adb shell 中打開 BookStore.db 數(shù)據(jù)庫吞歼,然后鍵入.table 命令圈膏,結(jié)果如圖所示:


由此可以看出, Category 表已經(jīng)創(chuàng)建成功了篙骡,同時也說明我們的升級功能的確起到了作用稽坤。

點此進入:GitHub開源項目“愛閱”

感謝優(yōu)秀的你跋山涉水看到了這里糯俗,歡迎關(guān)注下讓我們永遠在一起尿褪!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市得湘,隨后出現(xiàn)的幾起案子茫多,更是在濱河造成了極大的恐慌,老刑警劉巖忽刽,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件天揖,死亡現(xiàn)場離奇詭異,居然都是意外死亡跪帝,警方通過查閱死者的電腦和手機今膊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伞剑,“玉大人斑唬,你說我怎么就攤上這事±杵” “怎么了恕刘?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抒倚。 經(jīng)常有香客問我褐着,道長,這世上最難降的妖魔是什么托呕? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任含蓉,我火速辦了婚禮频敛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘馅扣。我一直安慰自己斟赚,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布差油。 她就那樣靜靜地躺著拗军,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蓄喇。 梳的紋絲不亂的頭發(fā)上发侵,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音公罕,去河邊找鬼。 笑死耀销,一個胖子當著我的面吹牛楼眷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播熊尉,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼罐柳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狰住?” 一聲冷哼從身側(cè)響起张吉,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎催植,沒想到半個月后肮蛹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡创南,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年伦忠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稿辙。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡昆码,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出邻储,到底是詐尸還是另有隱情赋咽,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布吨娜,位于F島的核電站脓匿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宦赠。R本人自食惡果不足惜亦镶,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一日月、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缤骨,春花似錦爱咬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虱歪,卻和暖如春蜂绎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笋鄙。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工师枣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萧落。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓践美,卻偏偏與公主長得像,于是被迫代替她去往敵國和親找岖。 傳聞我的和親對象是個殘疾皇子陨倡,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

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