SQLite簡介

SQLite簡介

一赞草、簡介

SQLite是一款輕量級的關系型數(shù)據(jù)庫讹堤,它的運算速度非常快厨疙, 占用資源很少洲守,通常只需要幾百K的內存就足夠了,因而特別適合在移動設備上使用。SQLite不僅支持標準的SQL語法梗醇,還遵循了數(shù)據(jù)庫的ACID事務知允。

SQLite的數(shù)據(jù)庫都是以單個文件的形式存在,這些數(shù)據(jù)都是以B-Tree的數(shù)據(jù)結構形式存儲在磁盤上叙谨。

在事務處理方面温鸽,SQLite通過數(shù)據(jù)庫級上的獨占性和共享鎖來實現(xiàn)獨立事務處理。這意味著多個進程可以在同一時間從同一數(shù)據(jù)庫讀取數(shù)據(jù)手负,但只有一個可以寫入數(shù)據(jù)涤垫。在某個進程或線程想數(shù)據(jù)庫執(zhí)行寫操作之前,必須獲得獨占鎖竟终。在獲得獨占鎖之后蝠猬,其他的讀或寫操作將不會再發(fā)生。

SQLite采用動態(tài)數(shù)據(jù)類型衡楞,當某個值插入到數(shù)據(jù)庫時吱雏,SQLite將會檢查它的類型,如果該類型與關聯(lián)的列不匹配瘾境,SQLite則會嘗試將該值轉換成該列的類型歧杏,如果不能轉換,則該值將作為本身的類型存儲迷守,SQLite稱這為“弱類型”犬绒。但有一個特例,如果是INTEGER PRIMARY KEY兑凿,則其類型不會被轉換凯力,會報一個“datatype missmatch”的錯誤。

概括來講礼华,SQLite支持NULL咐鹤、INTEGER、REAL圣絮、TEXT和BLOB數(shù)據(jù)類型祈惶,分別代表空值、整型值扮匠、浮點值捧请、字符串文本、二進制對象棒搜。

二疹蛉、使用

1.創(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ù)庫進行讀寫操作的對象谜慌。構建出SQLiteOpenHelper 的實例之后然想,再調用它的getReadableDatabase()或getWritableDatabase()方法就能夠創(chuàng)建數(shù)據(jù)庫了,數(shù)據(jù)庫文件會存放在/data/data/<package name>/databases/目錄下欣范。

SQLite不像其他的數(shù)據(jù)庫擁有眾多繁雜的數(shù)據(jù)類型变泄,它的數(shù)據(jù)類型很簡單,integer表示整型恼琼,real表示浮點型妨蛹,text表示文本類型,blob表示二進制類型晴竞。

2.查看數(shù)據(jù)庫

數(shù)據(jù)庫文件會存放在/data/data/<package name>/databases/目錄下蛙卤。通過adb shell進入到設備控制臺,然后使用cd命令進入到數(shù)據(jù)庫的目錄下噩死,用ls查看該目錄里的文件颤难,可以看到我們定義的數(shù)據(jù)庫文件,數(shù)據(jù)庫文件以".db"結尾已维。

借助SQLite命令來打開數(shù)據(jù)庫行嗤,執(zhí)行鍵入sqlite3 <數(shù)據(jù)庫名稱>即可,例如:

sqlite3 bookstore.db

這時就已經(jīng)打開了bookstore.db數(shù)據(jù)庫垛耳,現(xiàn)在就可以對這個數(shù)據(jù)庫中的表進行管理了昂验。 首先來看一下目前數(shù)據(jù)庫中有哪些表,鍵入.table 命令艾扮。

sqlite> .table

這里還可以通過.schema 命令來查看它們的建表語句。

sqlite > .schema

3.使用SQLiteOpenHelper對數(shù)據(jù)庫進行版本管理

為了實現(xiàn)對數(shù)據(jù)庫版本進行管理,Android系統(tǒng)為我們提供了一個名為SQLiteOpenHelper的抽象類占婉,必須繼承它才能使用泡嘴。SQLiteOpenHelper類提供了兩個重要的方法,分別是onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)逆济,前者用于初次使用軟件時生成數(shù)據(jù)庫表酌予,后者用于升級軟件時更新數(shù)據(jù)庫表結構磺箕。

當調用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法獲取用于操作數(shù)據(jù)庫的SQLiteDatabase實例的時候,如果數(shù)據(jù)庫不存在抛虫,android系統(tǒng)會自動生成一個數(shù)據(jù)庫松靡,接著調用onCreate()方法,onCreate()方法在初次生成數(shù)據(jù)庫時才會被調用建椰,在onCreate()方法里可以生成數(shù)據(jù)庫表結構及添加一些應用使用到的初始化數(shù)據(jù)雕欺。

onUpgrade()方法在數(shù)據(jù)庫的版本發(fā)生變化時會被調用,一般在軟件升級時才需改變版本號棉姐,而數(shù)據(jù)庫的版本是由程序員控制的屠列,假設數(shù)據(jù)庫現(xiàn)在的版本是1,由于業(yè)務的變更伞矩,修改了數(shù)據(jù)庫表結構笛洛,這時候就需要升級軟件,升級軟件時希望更新用戶手機里的數(shù)據(jù)庫表結構乃坤,為了實現(xiàn)這一目的苛让,可以把原來的數(shù)據(jù)庫版本設置為2,并且在onUpgrade()方法里面實現(xiàn)表結構的更新湿诊。當軟件的版本升級次數(shù)比較多狱杰,這時在onUpgrade()方法里面可以根據(jù)原版號和目標版本號進行判斷,然后作出相應的表結構及數(shù)據(jù)更新枫吧。

getWritableDatabase()和getReadableDatabase()方法都可以獲取一個用于操作數(shù)據(jù)庫的SQLiteDatabase實例浦旱。但getWritableDatabase() 方法以讀寫方式打開數(shù)據(jù)庫,一旦數(shù)據(jù)庫的磁盤空間滿了九杂,數(shù)據(jù)庫就只能讀而不能寫颁湖。getReadableDatabase()方法先以讀寫方式打開數(shù)據(jù)庫,如果數(shù)據(jù)庫的磁盤空間滿了例隆,就會打開失敗甥捺,當打開失敗后會繼續(xù)嘗試以只讀方式打開數(shù)據(jù)庫。

4.使用SQLiteDatabase操作SQLite數(shù)據(jù)庫

Android提供了一個名為SQLiteDatabase的類镀层,該類封裝了一些操作數(shù)據(jù)庫的API镰禾,使用該類可以完成對數(shù)據(jù)進行添加(Create)、查詢(Retrieve)唱逢、更新(Update)和刪除(Delete)操作(這些操作簡稱為CRUD)吴侦。對SQLiteDatabase的學習,我們應該重點掌握execSQL()和rawQuery()方法坞古。 execSQL()方法可以執(zhí)行insert备韧、delete、update和CREATE TABLE之類有更改行為的SQL語句痪枫; rawQuery()方法用于執(zhí)行select語句织堂。

execSQL()方法的使用例子:

SQLiteDatabase db = ....;  

db.execSQL("insert into person(name, age) values('炸死特', 4)");  

db.close();  

在拼接SQL語句時叠艳,要注意一些特殊符號,例如單引號易阳,“&”符號附较,為了保證組拼好的SQL語句語法正確,必須對SQL語句中的這些特殊SQL符號都進行轉義潦俺,顯然拒课,對每條SQL語句都做這樣的處理工作是比較煩瑣的。 SQLiteDatabase類提供了一個重載后的execSQL(String sql, Object[] bindArgs)方法黑竞,使用這個方法可以解決前面提到的問題捕发,因為這個方法支持使用占位符參數(shù)(?)。

SQLiteDatabase db = ....;  

db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"炸死特", 4});   

db.close();  

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

SQLiteDatabase的rawQuery() 用于執(zhí)行select語句遏匆,使用例子如下:

SQLiteDatabase db = ....;  

Cursor cursor = db.rawQuery(“select * from person”, null);  

while (cursor.moveToNext()) {  

    int personid = cursor.getInt(0); //獲取第一列的值,第一列的索引從0開始  

    String name = cursor.getString(1);//獲取第二列的值  

    int age = cursor.getInt(2);//獲取第三列的值  

}  

cursor.close();  

db.close();  

rawQuery()方法的第一個參數(shù)為select語句法挨;第二個參數(shù)為select語句中占位符參數(shù)的值,如果select語句沒有使用占位符幅聘,該參數(shù)可以設置為null凡纳。帶占位符參數(shù)的select語句使用例子如下:

Cursor cursor = db.rawQuery("select * from person where name like ? and age=?", new String[]{"%炸死特%", "4"});  

Cursor是結果集游標,用于對結果集進行隨機訪問帝蒿。使用moveToNext()方法可以將游標從當前行移動到下一行荐糜,如果已經(jīng)移過了結果集的最后一行,返回結果為false葛超,否則為true暴氏。另外Cursor還有常用的moveToPrevious()方法(用于將游標從當前行移動到上一行,如果已經(jīng)移過了結果集的第一行绣张,返回值為false答渔,否則為true)、moveToFirst()方法(用于將游標移動到結果集的第一行侥涵,如果結果集為空沼撕,返回值為false,否則為true)和moveToLast()方法(用于將游標移動到結果集的最后一行,如果結果集為空,返回值為false,否則為true ) 。

除了前面介紹的execSQL()和rawQuery()方法托猩,SQLiteDatabase還專門提供了對應于添加、刪除、更新兄世、查詢的操作方法: insert()敬拓、delete()、update()和query()裙戏。這些方法實際上是給那些不太了解SQL語法的人使用的乘凸,對于熟悉SQL語法的程序員而言,直接使用execSQL()和rawQuery()方法執(zhí)行SQL語句就能完成數(shù)據(jù)的添加累榜、刪除营勤、更新、查詢操作壹罚。

insert()方法的使用

SQLiteDatabase db = databaseHelper.getWritableDatabase();  

ContentValues values = new ContentValues();  

values.put("name", "炸死特");  

values.put("age", 4);  

long rowid = db.insert(“person”, null, values);//返回新添記錄的行號葛作,與主鍵id無關

不管第三個參數(shù)是否包含數(shù)據(jù),執(zhí)行Insert()方法必然會添加一條記錄猖凛,如果第三個參數(shù)為空赂蠢,會添加一條除主鍵之外其他字段值為Null的記錄。

delete()方法的使用

SQLiteDatabase db = databaseHelper.getWritableDatabase();  

db.delete("person", "personid<?", new String[]{"2"});  

db.close(); 

上面代碼用于從person表中刪除personid小于2的記錄辨泳。

update()方法的使用

SQLiteDatabase db = databaseHelper.getWritableDatabase();  

ContentValues values = new ContentValues();  

values.put(“name”, “炸死特”);//key為字段名虱岂,value為值  

db.update("person", values, "personid=?", new String[]{"1"});   

db.close();  

上面代碼用于把person表中personid等于1的記錄的name字段的值改為“炸死特”。

query()方法的使用

SQLiteDatabase db = databaseHelper.getWritableDatabase();  

Cursor cursor = db.query("person", new String[]{"personid,name,age"}, "name like ?", new String[]{"%炸死特%"}, null, null, "personid desc", "1,2");  

while (cursor.moveToNext()) {  

     int personid = cursor.getInt(0); //獲取第一列的值,第一列的索引從0開始  

      String name = cursor.getString(1);//獲取第二列的值  

      int age = cursor.getInt(2);//獲取第三列的值  

}  

cursor.close();  

db.close();  

實際上是把select語句拆分成了若干個組成部分菠红,然后作為方法的輸入?yún)?shù)第岖。

query(table, columns, selection, selectionArgs, groupBy, having, orderBy,limit)方法各參數(shù)的含義:

table:表名。相當于select語句from關鍵字后面的部分试溯。如果是多表聯(lián)合查詢蔑滓,可以用逗號將兩個表名分開。

columns:要查詢出來的列名遇绞。相當于select語句select關鍵字后面的部分键袱。

selection:查詢條件子句,相當于select語句where關鍵字后面的部分试读,在條件子句允許使用占位符“?”

selectionArgs:對應于selection語句中占位符的值杠纵,值在數(shù)組中的位置與占位符在語句中的位置必須一致,否則就會有異常钩骇。

groupBy:相當于select語句group by關鍵字后面的部分

having:相當于select語句having關鍵字后面的部分

orderBy:相當于select語句order by關鍵字后面的部分比藻,如:personid desc, age asc;

limit:指定偏移量和獲取的記錄數(shù),相當于select語句limit關鍵字后面的部分倘屹。

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

一般的數(shù)據(jù)采用的固定的靜態(tài)數(shù)據(jù)類型银亲,而SQLite采用的是動態(tài)數(shù)據(jù)類型,會根據(jù)存入值自動判斷纽匙。SQLite具有以下五種常用的數(shù)據(jù)類型:

NULL: 這個值為空值

VARCHAR(n):長度不固定且其最大長度為n的字串务蝠,n不能超過4000。

CHAR(n):長度固定為n的字串烛缔,n不能超過254馏段。

INTEGER: 值被標識為整數(shù),依據(jù)值的大小可以依次被存儲為1,2,3,4,5,6,7,8.

REAL: 所有值都是浮動的數(shù)值,被存儲為8字節(jié)的IEEE浮動標記序號.

TEXT: 值為文本字符串,使用數(shù)據(jù)庫編碼存儲(TUTF-8, UTF-16BE or UTF-16-LE).

BLOB: 值是BLOB數(shù)據(jù)塊轩拨,以輸入的數(shù)據(jù)格式進行存儲。如何輸入就如何存儲,不改變格式院喜。

DATA:包含了年份亡蓉、月份、日期喷舀。

TIME: 包含了小時砍濒、分鐘、秒硫麻。

三爸邢、源碼分析

SQLiteOpenHelper是SQLiteDatabase的一個輔助類,用來幫助在應用初始化時創(chuàng)建數(shù)據(jù)庫表以及應用升級時更新數(shù)據(jù)庫表拿愧。下面將從源碼的角度來分析SQLiteOpenHelper的getReadableDatabase()和gegetWritableDatabase()方法杠河。

3.1 SQLiteOpenHelper()

public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version)
{
    this(context, name, factory, version, null);
}

public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
        DatabaseErrorHandler errorHandler) {
    //版本小于1,則會拋出異常赶掖,因為初始創(chuàng)建的數(shù)據(jù)庫版本為0感猛,只有版本大于0時,才會觸發(fā)調用onCreate()方法
    if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);

    mContext = context;
    mName = name;
    mFactory = factory;
    mNewVersion = version;
    mErrorHandler = errorHandler;
}

SQLiteOpenHelper的構造函數(shù)主要是保存參數(shù)傳遞的變量奢赂,為后面創(chuàng)建或打開數(shù)據(jù)庫做準備陪白。

3.2 SQLiteOpenHelper.getReadableDatabase()

public SQLiteDatabase getReadableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(false);
    }
}

private SQLiteDatabase getDatabaseLocked(boolean writable) {
    //如果數(shù)據(jù)庫不為空,并且已經(jīng)打開了膳灶,則直接返回
    if (mDatabase != null) {
        if (!mDatabase.isOpen()) {
            // Darn!  The user closed the database by calling mDatabase.close().
            mDatabase = null;
        } else if (!writable || !mDatabase.isReadOnly()) {
            // The database is already open for business.
            return mDatabase;
        }
    }

    // 如果正在初始化咱士,則拋出異常
    if (mIsInitializing) {
        throw new IllegalStateException("getDatabase called recursively");
    }

    SQLiteDatabase db = mDatabase;
    try {
        mIsInitializing = true;

        if (db != null) {
            //writable為false
            if (writable && db.isReadOnly()) {
                db.reopenReadWrite();
            }
        } else if (mName == null) {//數(shù)據(jù)庫的名字,則創(chuàng)建一個空的數(shù)據(jù)庫
            db = SQLiteDatabase.create(null);
        } else {
            try {
                if (DEBUG_STRICT_READONLY && !writable) {//調試時信息
                    final String path = mContext.getDatabasePath(mName).getPath();
                    db = SQLiteDatabase.openDatabase(path, mFactory,
                            SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                } else {
                    db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
                            Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
                            mFactory, mErrorHandler);//打開或者創(chuàng)建數(shù)據(jù)庫
                }
            } catch (SQLiteException ex) {
                if (writable) {
                    throw ex;
                }
                Log.e(TAG, "Couldn't open " + mName
                        + " for writing (will try read-only):", ex);
                final String path = mContext.getDatabasePath(mName).getPath();
                db = SQLiteDatabase.openDatabase(path, mFactory,
                        SQLiteDatabase.OPEN_READONLY, mErrorHandler);//如果以PRIVATE模式打開數(shù)據(jù)時拋出了異常轧钓,則以只讀的方式打開數(shù)據(jù)庫文件
            }
        }

        onConfigure(db);//配置數(shù)據(jù)庫的一些信息序厉,默認實現(xiàn)為空,在onCreate()毕箍、onUpdate()方法之前調用

        final int version = db.getVersion();//獲取數(shù)據(jù)庫的版本號弛房,剛創(chuàng)建出來的數(shù)據(jù)庫的版本號為0
        if (version != mNewVersion) {//版本號發(fā)生了變化
            if (db.isReadOnly()) {//如果是數(shù)據(jù)庫是只讀的,則拋出異常而柑,因為只讀數(shù)據(jù)庫不能被升級
                throw new SQLiteException("Can't upgrade read-only database from version " +
                        db.getVersion() + " to " + mNewVersion + ": " + mName);
            }

            db.beginTransaction();//以事務的方式執(zhí)行
            try {
                if (version == 0) {//初始版本為0文捶,說明是第一次打開或者創(chuàng)建數(shù)據(jù)庫
                    onCreate(db);//回調onCreate()方法
                } else {
                    if (version > mNewVersion) {//如果新的版本號比舊的版本號小,則把數(shù)據(jù)庫進行降級處理
                        onDowngrade(db, version, mNewVersion);//降級處理
                    } else {//如果新的版本號比舊的版本號大媒咳,則把數(shù)據(jù)庫進行升級處理
                        onUpgrade(db, version, mNewVersion);//升級處理
                    }
                }
                db.setVersion(mNewVersion);//設置新的版本號
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }

        onOpen(db);//在數(shù)據(jù)庫打開之后調用粹排,默認實現(xiàn)為空

        if (db.isReadOnly()) {
            Log.w(TAG, "Opened " + mName + " in read-only mode");
        }

        mDatabase = db;//保存打開的數(shù)據(jù)庫文件
        return db;
    } finally {
        mIsInitializing = false;
        if (db != null && db != mDatabase) {
            db.close();
        }
    }
}

可以看到,通過getReadableDatabase()方式打開數(shù)據(jù)庫時涩澡,傳遞的writeable參數(shù)為false顽耳,最終調用的是getDatabaseLocked()方法進行打開數(shù)據(jù)庫操作。在getDatabaseLocked()方法中的主要的操作有:

  1. getDatabaseLocked()方法的調用是在獲取獨占鎖synchronized后調用的,因此可以保證每次只有一個線程去操作數(shù)據(jù)庫文件射富。

  2. 如果數(shù)據(jù)庫文件已經(jīng)存在了膝迎,并且已經(jīng)打開了,則直接返回該數(shù)據(jù)庫胰耗;

  3. 根據(jù)初始化SQLiteOpenHelper時設置的數(shù)據(jù)庫名字弄抬,以PRIVATE模式去打開或者創(chuàng)建一個數(shù)據(jù)庫文件。如果打開數(shù)據(jù)庫文件失敗宪郊,則嘗試以只讀方式打開數(shù)據(jù)庫文件;

  4. 數(shù)據(jù)庫連接完成之后拖陆,可以調用onConfigure()方法對數(shù)據(jù)庫進行配置操作弛槐,默認該方法是空實現(xiàn);

  5. 根據(jù)數(shù)據(jù)庫版本號依啰,決定是調用onCreate()方法還是調用onDowngrade()或者是onUpgrade()方法:

    1. 如果初始化數(shù)據(jù)庫版本號為0乎串,則表示是第一次打開或者創(chuàng)建數(shù)據(jù)庫,則調用onCreate()方法速警;
    2. 如果新的數(shù)據(jù)庫版本號大于舊的數(shù)據(jù)庫版本號叹誉,則調用onUpgrade()方法,對數(shù)據(jù)庫進行升級操作闷旧;
    3. 如果新的數(shù)據(jù)庫版本號小于舊的數(shù)據(jù)庫版本號长豁,則調用onDowngrade()方法,對數(shù)據(jù)庫進行降級操作忙灼;
  6. 數(shù)據(jù)庫打開之后匠襟,可以調用onOpen()方法進行一些配置操作,該方法模式是空實現(xiàn)该园;

3.3 SQLiteOpenHelper.getWritableDatabase()

public SQLiteDatabase getWritableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(true);//見3.2
    }
}

可以看到酸舍,getWritableDatabase()和通過getReadableDatabase()方法最終都是調用getDatabaseLocked()方法,只是傳遞的wirteable參數(shù)不同里初。getReadableDatabase()方法傳遞的writeable參數(shù)為false啃勉,而getWritableDatabase()傳遞的參數(shù)為true。

四双妨、例子

SQLite使用的Demo:SQLiteDemo

五淮阐、參考

android之存儲篇_SQLite數(shù)據(jù)庫

Androird SQLite應用詳解

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市斥难,隨后出現(xiàn)的幾起案子枝嘶,更是在濱河造成了極大的恐慌,老刑警劉巖哑诊,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件群扶,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機竞阐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門缴饭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人骆莹,你說我怎么就攤上這事颗搂。” “怎么了幕垦?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵丢氢,是天一觀的道長。 經(jīng)常有香客問我先改,道長疚察,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任仇奶,我火速辦了婚禮貌嫡,結果婚禮上,老公的妹妹穿的比我還像新娘该溯。我一直安慰自己岛抄,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布狈茉。 她就那樣靜靜地躺著夫椭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪论皆。 梳的紋絲不亂的頭發(fā)上益楼,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音点晴,去河邊找鬼感凤。 笑死,一個胖子當著我的面吹牛粒督,可吹牛的內容都是我干的陪竿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼屠橄,長吁一口氣:“原來是場噩夢啊……” “哼族跛!你這毒婦竟也來了?” 一聲冷哼從身側響起锐墙,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤礁哄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后溪北,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桐绒,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡夺脾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了茉继。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咧叭。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖烁竭,靈堂內的尸體忽然破棺而出菲茬,到底是詐尸還是另有隱情,我是刑警寧澤派撕,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布婉弹,位于F島的核電站,受9級特大地震影響终吼,放射性物質發(fā)生泄漏马胧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一衔峰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛙粘,春花似錦垫卤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至舔痕,卻和暖如春评抚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伯复。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工慨代, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啸如。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓侍匙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叮雳。 傳聞我的和親對象是個殘疾皇子想暗,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容

  • 一.SQLite的介紹1.SQLite簡介SQLite是一款輕型的數(shù)據(jù)庫,是遵守ACID的關聯(lián)式數(shù)據(jù)庫管理系統(tǒng)帘不,它...
    AiPuff閱讀 616評論 0 3
  • 本篇文章可以學到以下內容: SQLite操作以及SQL基本語法 Android中的數(shù)據(jù)庫操作(增刪改查) Andr...
    寶塔山上的貓閱讀 17,534評論 5 114
  • LZ-Says:給大家推薦一個網(wǎng)站说莫,有興趣可以查閱,想為大家貢獻一點自己的力量也可以投稿寞焙,老大審核通過會發(fā)表储狭,更好...
    靜心Study閱讀 954評論 0 3
  • 廖單第20周開始啦互婿!《We're Going On a Bear Hunt》和《Creepy Crawly Cal...
    Jenny2011閱讀 133評論 0 0
  • 咋看陽光是七彩的 陽光下的你們笑靨如花 歡聲笑語的討論動靜脈考題 咕嚕咕嚕,誰的肚子再唱歌 你晶密,你擒悬,她,餓啦稻艰,吃飯去
    戚六閱讀 198評論 1 0