數(shù)據(jù)存儲與訪問之——SQLite數(shù)據(jù)庫

SQLite數(shù)據(jù)庫险毁,和其他的SQL數(shù)據(jù)庫不同制圈, 我們并不需要在手機上另外安裝一個數(shù)據(jù)庫軟件,Android系統(tǒng)已經(jīng)集成了這個數(shù)據(jù)庫辱揭。

SQLite有什么特點

SQlite通過文件來保存數(shù)據(jù)庫离唐,一個文件就是一個數(shù)據(jù)庫,數(shù)據(jù)庫中又包含多個表格问窃,表格里又有 多條記錄亥鬓,每個記錄由多個字段構(gòu)成,每個字段有對應(yīng)的域庇,每個值我們可以指定類型嵌戈,也可以不指定 類型(主鍵除外)

為什么要用SQLite:

SP是一種輕量級數(shù)據(jù)存儲方式,存儲一些跟賬號密碼個人信息相關(guān)的數(shù)據(jù)听皿,如果數(shù)據(jù)繁雜這時候就要用到SQLite存儲以提高數(shù)據(jù)存取得效率熟呛。

幾個相關(guān)的類:

SQLiteOpenHelper

:抽象類,我們通過繼承該類尉姨,然后重寫數(shù)據(jù)庫創(chuàng)建以及更新的方法庵朝, 我們還可以通過該類的對象獲得數(shù)據(jù)庫實例,或者關(guān)閉數(shù)據(jù)庫又厉!

SQLiteDatabase:數(shù)據(jù)庫訪問類:我們可以通過該類的對象來對數(shù)據(jù)庫做一些增刪改查的操作

Cursor:游標(biāo)九府,有點類似于JDBC里的resultset,結(jié)果集覆致!可以簡單理解為指向數(shù)據(jù)庫中某 一個記錄的指針侄旬!可以通過Cursor對數(shù)據(jù)進(jìn)行一行一行查詢的操作

?

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

安卓給我們提供了SQLiteOpenHelper的兩個方法, onCreate( )與onUpgrade( )來實現(xiàn)

onCreate(database)

:首次使用軟件時生成數(shù)據(jù)庫表

onUpgrade(database,oldVersion,newVersion)

:在數(shù)據(jù)庫的版本發(fā)生變化時會被調(diào)用, 一般在軟件升級時才需改變版本號煌妈,而數(shù)據(jù)庫的版本是由程序員控制的儡羔,假設(shè)數(shù)據(jù)庫現(xiàn)在的 版本是1宣羊,由于業(yè)務(wù)的變更,修改了數(shù)據(jù)庫表結(jié)構(gòu)汰蜘,這時候就需要升級軟件仇冯,升級軟件時希望 更新用戶手機里的數(shù)據(jù)庫表結(jié)構(gòu),為了實現(xiàn)這一目的鉴扫,可以把原來的數(shù)據(jù)庫版本設(shè)置為2 或者其他與舊版本號不同的數(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ù)庫第一次創(chuàng)建時被調(diào)用
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE person(personid INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20))");

}
//軟件版本號發(fā)生改變時調(diào)用
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("ALTER TABLE person ADD phone VARCHAR(12) NULL");
}
}

?

我們會創(chuàng)建這個my.db的文件澈缺,并且會執(zhí)行onCreate()里的方法坪创, 創(chuàng)建一個Person的表,他又兩個字段姐赡,主鍵personId和name字段莱预;接著如我我們修改db的版本 號,那么下次啟動就會調(diào)用onUpgrade()里的方法项滑,往表中再插入一個字段依沮!另外這里是插入 一個字段,所以數(shù)據(jù)不會丟失枪狂,如果是重建表的話危喉,表中的數(shù)據(jù)會全部丟失(下面數(shù)據(jù)庫更新解決)

?

流程:

Step 1:自定義一個類繼承SQLiteOpenHelper類

Step 2:在該類的構(gòu)造方法的super中設(shè)置好要創(chuàng)建的數(shù)據(jù)庫名,版本號

Step 3:重寫onCreate( )方法創(chuàng)建表結(jié)構(gòu)

Step 4:重寫onUpgrade( )方法定義版本號發(fā)生改變后執(zhí)行的操作

使用Android提供的API操作SQLite

?

db = myDBHelper.getWritableDatabase();
switch (v.getId()) {
case R.id.btn_insert:
ContentValues values1 = new ContentValues();
values1.put("name", "呵呵~" + i);
i++;
//參數(shù)依次是:表名,強行插入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條件,以及約束毒姨,如果不指定三四兩個參數(shù)哑蔫,會更改所有行
db.update("person", values2, "name = ?", new String[]{"呵呵~2"});
break;
case R.id.btn_delete:
//參數(shù)依次是表名,以及where條件與約束
db.delete("person", "personid = ?", new String[]{"3"});
break;
}

?

?

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

?

不想用Android提供的這些API弧呐, 你可以直接使用SQLiteDatabase給我們提供的相關(guān)方法:

execSQL(SQL,Object[]):使用帶占位符的SQL語句,這個是執(zhí)行修改數(shù)據(jù)庫內(nèi)容的sql語句用的

rawQuery(SQL,Object[]):使用帶占位符的SQL查詢操作 另外前面忘了介紹下Curosr這個東西以及相關(guān)屬性闸迷,這里補充下: ——

Cursor:對象有點類似于JDBC中的ResultSet,結(jié)果集!使用差不多,提供一下方法移動查詢結(jié)果的記錄指針:

move(offset):指定向上或者向下移動的行數(shù),整數(shù)表示向下移動;負(fù)數(shù)表示向上移動!

moveToFirst():指針移動到第一行,成功返回true,也說明有數(shù)據(jù)

moveToLast():指針移動到最后一樣,成功返回true;

moveToNext():指針移動到下一行,成功返回true,表明還有元素泉懦!

moveToPrevious():移動到上一條記錄

getCount( )獲得總得數(shù)據(jù)條數(shù)

isFirst():是否為第一條記錄

isLast():是否為最后一項

moveToPosition(int):移動到指定行

使用代碼示例:

//插入數(shù)據(jù)
public void save(Customer customer){
SQLiteDatabase sqLiteDatabase=dbHelper.getWritableDatabase();
sqLiteDatabase.execSQL("INSERT INTO customer(customerName,deliveryPhone) values(?,?)",new String[]{customer.getCustomerName(),customer.getDeliveryPhone()});
}
//刪除數(shù)據(jù)
public void delete(Integer customerid){
SQLiteDatabase sqLiteDatabase=dbHelper.getWritableDatabase();
sqLiteDatabase.execSQL("DELETE FROM customer WHERE customerid=?",new Integer[]{customerid});
}

//更新數(shù)據(jù)
public void updata(Customer customer){
SQLiteDatabase sqLiteDatabase=dbHelper.getWritableDatabase();
sqLiteDatabase.execSQL("UPDATA customer SET customerName=?,deliveryPhone=? WHERE customerid=?",new String[]{customer.getCustomerName(),customer.getDeliveryPhone(), String.valueOf(customer.getCustomerId())});
}
//查詢數(shù)據(jù)

public Customer select(Integer customerid){
SQLiteDatabase sqLiteDatabase=dbHelper.getReadableDatabase();
Cursor cursor=sqLiteDatabase.rawQuery("SELECT * FROM customer WHERE customerid=?",new String[]{customerid.toString()});
//存在數(shù)據(jù)才返回true
if(cursor.moveToFirst()){
int id=cursor.getInt(cursor.getColumnIndex("customerid"));
String name=cursor.getString(cursor.getColumnIndex("customerName"));
String phone=cursor.getString(cursor.getColumnIndex("deliveryPhone"));
return new Customer(id,name,phone);
}
cursor.close();
return null;
}
//.數(shù)據(jù)分頁查詢
public List<Customer> getScrollData(int offset, int maxResult)
{
List<Customer> person = new ArrayList<Customer>();
SQLiteDatabase db = dbHelper.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 id = cursor.getInt(cursor.getColumnIndex("personid"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String phone = cursor.getString(cursor.getColumnIndex("phone"));
person.add(new Customer(id,name,phone)) ;
}
cursor.close();
return person;
}
//查詢記錄數(shù)
public long getCount()
{
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT COUNT (*) FROM customer",null);
cursor.moveToFirst();
long result = cursor.getLong(0);
cursor.close();
return result;
}

?

除了上面獲取條數(shù)的方法外還可以使用cursor.getCount()方法獲得數(shù)據(jù)的條數(shù), 但是SQL語句要改改稿黍!比如SELECT * FROM customer;

?

SQLite事務(wù)

多個操作捆綁在一起勋又,只有所有操作都執(zhí)行完畢于个,事務(wù)才會生效,如果其中有一個操作未執(zhí)行完畢邦投,之前所有操作都會撤銷。

?

方法:

beginTransaction():開啟事務(wù)

endTransaction():結(jié)束事務(wù)

setTransactionSuccessful():結(jié)束事務(wù)有兩張方式酣栈,事務(wù)回滾或者事務(wù) 提交险胰,默認(rèn)為false撤銷,如果提交設(shè)置為true.

簡單點說就是:寫在事務(wù)里的所有數(shù)據(jù)庫操作都成功矿筝,事務(wù)提交起便,否則,事務(wù)回滾到原始狀態(tài)

SQLite存儲大二進(jìn)制文件

一般我們很少往數(shù)據(jù)庫中存儲大二進(jìn)制文件窖维,比如圖片榆综,音頻,視頻等铸史,對于這些我們一般 是存儲文件路徑鼻疮,但總會有些奇葩的需求。以圖片為例子琳轿,將圖片保存到SQLite中判沟,以及讀取SQLite中的圖片!

?

1.保存圖片到Sqlite中:

(1)創(chuàng)建數(shù)據(jù)庫表的時候崭篡,需要創(chuàng)建一個BLOB的字段挪哄,用于存儲二進(jìn)制值。

sqLiteDatabase.execSQL("CREATE TABLE test(_id INTEGER PRIMARY KEY AUTOINCREAMENT,head_img BLOB)");

(2)將圖片轉(zhuǎn)換成BLOB格式(這里是ImageView為例琉闪,如果是普通圖片只需要轉(zhuǎn)換成Bitmap再調(diào)用即可)

try {
SQLiteDatabase sqLiteDatabase=dbHelper.getWritableDatabase();
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
//壓縮為PNG格式迹炼,100標(biāo)示跟原圖大小一致
(BitmapDrawable)imageView.getDrawable().getBitmap().compress(Bitmap.CompressFormat.PNG,100,byteArrayOutputStream);
Object[] objects=new Object[]{byteArrayOutputStream.toByteArray()};
sqLiteDatabase.execSQL("INSERT INTO test(head_img) values(?)",objects);
byteArrayOutputStream.close();
sqLiteDatabase.close();
} catch (IOException e) {
e.printStackTrace();
}

?

讀取SQLite中的圖片

//讀取SQLIte中的圖片
SQLiteDatabase sqLiteDatabase=dbHelper.getReadableDatabase();
Cursor cursor=sqLiteDatabase.rawQuery("SELECT head_img FROM text",null);
if(cursor!=null){
if(cursor.moveToFirst()){
//取出圖片保存到字節(jié)數(shù)組中
byte[] img=cursor.getBlob(cursor.getColumnIndex("head_img"));
//將圖片顯示到Imageview上面
if(img!=null){
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(img);
imageView.setImageDrawable(Drawable.createFromStream(byteArrayInputStream,"img"));
}
}
cursor.close();
}

?

數(shù)據(jù)庫升級

假如我們已經(jīng)升級到第三個版本了,我們在第二個版本增加了一個表塘偎,

然后第三個版本也增加了一個表疗涉,加入用戶直接從第一個版本升級到第三個版本,這樣

沒經(jīng)過第二個版本吟秩,就沒有增加的那個表咱扣,這可怎么破?

?

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

private void upgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > oldVersion) {
if (oldVersion + 1 == newVersion) {
switch (oldVersion) {
case 1:
upgrade1_2(db);
break;
case 2:
upgrade2_3(db);
break;
case 3:
upgrade3_4(db);
case 4:
upgrade4_5(db);
case 5:
upgrade5_6(db);
case 6:
upgrade6_7(db);
case 7:
upgrade7_8(db);
case 8:
upgrade8_9(db);
default:
break;
}
return;
}
upgrade(db, oldVersion, newVersion - 1);
upgrade(db, newVersion - 1, newVersion);
}
}

private void upgrade1_2(SQLiteDatabase db) {
String sql = "drop table storage;";
db.execSQL(sql);
db.execSQL(storageSQL);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涵防,一起剝皮案震驚了整個濱河市闹伪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌壮池,老刑警劉巖偏瓤,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異椰憋,居然都是意外死亡厅克,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門橙依,熙熙樓的掌柜王于貴愁眉苦臉地迎上來证舟,“玉大人硕旗,你說我怎么就攤上這事∨穑” “怎么了漆枚?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抵知。 經(jīng)常有香客問我墙基,道長,這世上最難降的妖魔是什么刷喜? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任残制,我火速辦了婚禮,結(jié)果婚禮上吱肌,老公的妹妹穿的比我還像新娘痘拆。我一直安慰自己,他們只是感情好氮墨,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吐葵,像睡著了一般规揪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上温峭,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天猛铅,我揣著相機與錄音,去河邊找鬼凤藏。 笑死奸忽,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的揖庄。 我是一名探鬼主播栗菜,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蹄梢!你這毒婦竟也來了疙筹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤禁炒,失蹤者是張志新(化名)和其女友劉穎而咆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幕袱,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡暴备,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了们豌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涯捻。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡阁危,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汰瘫,到底是詐尸還是另有隱情狂打,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布混弥,位于F島的核電站趴乡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蝗拿。R本人自食惡果不足惜晾捏,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哀托。 院中可真熱鬧惦辛,春花似錦、人聲如沸仓手。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗽冒。三九已至呀伙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間添坊,已是汗流浹背剿另。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贬蛙,地道東北人雨女。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像阳准,于是被迫代替她去往敵國和親氛堕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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

  • 一溺职、環(huán)境 安卓系統(tǒng):4.2 操作系統(tǒng):Win 8.1 工具:Android Studio 二岔擂、SQLite操作 新...
    谷鴿不愛吃稻谷閱讀 570評論 0 2
  • LZ-Says:給大家推薦一個網(wǎng)站,有興趣可以查閱浪耘,想為大家貢獻(xiàn)一點自己的力量也可以投稿乱灵,老大審核通過會發(fā)表,更好...
    靜心Study閱讀 940評論 0 3
  • 不知道我要說什么七冲,就是想說點什么壓抑在心里的一些東西痛倚。從來不想表現(xiàn)出來,可是一些東西發(fā)現(xiàn)只會越埋越深澜躺,越埋越重蝉稳。 ...
    JeanWang閱讀 250評論 0 1
  • 1抒蚜、你若繼續(xù)做過去的事,你將一直是過去的你耘戚。以自我為中心的人所拆毀的嗡髓,以他人為中心的人可以重建。忍耐不僅是忍受困難...
    有長進(jìn)閱讀 378評論 5 1
  • shiro作為安全框架收津,提供了各種安全策略饿这,此處記錄身份認(rèn)證Authentication的簡單搭建步驟: (1)創(chuàng)...
    0d7bc1a914a4閱讀 241評論 0 0