Android數(shù)據(jù)庫(kù)
什么情況下我們才用數(shù)據(jù)庫(kù)做數(shù)據(jù)存儲(chǔ)?
大量數(shù)據(jù)結(jié)構(gòu)相同的數(shù)據(jù)需要存儲(chǔ)時(shí)了袁。Android內(nèi)置了sqlite坦仍,輕量級(jí)。
創(chuàng)建數(shù)據(jù)庫(kù)的方法
- 創(chuàng)建一個(gè)類繼承SqliteOpenHelper得糜,需要添加一個(gè)構(gòu)造方法敬扛,實(shí)現(xiàn)兩個(gè)方法oncreate ,onupgrade。
package com.example.databasedemo;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MyDatabaseOpenHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table book ("
+ "_id integer primary key autoincrement, name varchar(20), telephone varchar(11))";
public static final String NEW_TABLE = "create table book ("
+ "_id integer primary key autoincrement, price real, pages integer)";
private Context mContext;
/**
*
* @param context 上下文
* @param name 數(shù)據(jù)庫(kù)的名稱
* @param factory 用來(lái)創(chuàng)建cursor對(duì)象朝抖,填入null使用默認(rèn)的
* @param version version:數(shù)據(jù)庫(kù)的版本號(hào)啥箭,從1開(kāi)始,如果發(fā)生改變槽棍,onUpgrade方法將會(huì)調(diào)用,4.0之后只能升不能降
*/
public MyDatabaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
// 調(diào)用getReadableDatabase()或者getWritableDatabase()時(shí)會(huì)調(diào)用該方法
// 第一次創(chuàng)建數(shù)據(jù)庫(kù)時(shí)才能執(zhí)行該方法捉蚤,特別適合做表結(jié)構(gòu)的初始化
// 傳入的參數(shù)db可以用來(lái)執(zhí)行sql語(yǔ)句
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(NEW_TABLE); // 另外一個(gè)表
}
// version改變時(shí),調(diào)用這個(gè)方法炼七,version只能升不能降
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 先刪除缆巧,不刪除就onCreate發(fā)現(xiàn)表存在會(huì)報(bào)錯(cuò)。
// 若是表被刪除了豌拙,或者oncreate里面又新建一個(gè)表陕悬。因?yàn)橹耙呀?jīng)創(chuàng)建了aa.db,onCreate()方法不會(huì)得到執(zhí)行按傅。則不能創(chuàng)建成功捉超。所以這里需要?jiǎng)h除后再重建
db.execSQL("drop table if exists book");
db.execSQL("drop table if exists people");
// 強(qiáng)制執(zhí)行onCreate
onCreate(db);
db.execSQL("alter table book add author varchar(20)");
db.execSQL("alter table people add age integer");
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
// 1.創(chuàng)建一個(gè)幫助類的對(duì)象胧卤,調(diào)用getReadableDatabase方法,返回一個(gè)SqliteDatebase對(duì)象
MyDatabaseOpenHelper dbHelper = new MyDatabaseOpenHelper(mContext, "demo.db", null, 15);
// 創(chuàng)建數(shù)據(jù)庫(kù)拼岳,有則打開(kāi)枝誊,沒(méi)有則create
dbHelper.getReadableDatabase();
}
幫助類對(duì)象中的getWritableDatabase 和 getReadableDatabase都可以幫助我們獲取一個(gè)數(shù)據(jù)庫(kù)操作對(duì)象SqliteDatabase。
區(qū)別:
- getReadableDatabase: 先嘗試以讀寫方式打開(kāi)數(shù)據(jù)庫(kù)惜纸,如果磁盤空間滿了叶撒,他會(huì)重新嘗試以只讀方式打開(kāi)數(shù)據(jù)庫(kù)。
- getWritableDatabase: 直接以讀寫方式打開(kāi)數(shù)據(jù)庫(kù)耐版,如果磁盤空間滿了祠够,就直接報(bào)錯(cuò)。
數(shù)據(jù)庫(kù)的CURD - 1
- 對(duì)上面建的book表進(jìn)行CURD
- 使用上面的MyDatabaseOpenHelper創(chuàng)建數(shù)據(jù)庫(kù)和表粪牲。
- 封裝一個(gè)InfoBean來(lái)存儲(chǔ)表的信息古瓤。
- 封裝一個(gè)InfoDao來(lái)返回一個(gè)MyDatabaseOpenHelper,以及執(zhí)行增刪改查操作腺阳。
bean用來(lái)封裝表的數(shù)據(jù)落君。
package com.example.databasedemo.dao;
// 這些變量名和建表時(shí)候字段對(duì)應(yīng)
public class InfoBean {
public int _id;
public String name;
public int age;
public String telephone;
}
封裝好的執(zhí)行增刪改查的類,注意db不要隨便db.close
亭引,容易引發(fā)錯(cuò)誤叽奥。
package com.example.databasedemo.dao;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.example.databasedemo.MyDatabaseOpenHelper;
public class InfoDao {
private MyDatabaseOpenHelper myDatabaseOpenHelper;
private SQLiteDatabase db;
public InfoDao(Context context ,String dbName , int version){
//創(chuàng)建一個(gè)幫助類對(duì)象
myDatabaseOpenHelper = new MyDatabaseOpenHelper(context, dbName, null, version);
db = myDatabaseOpenHelper.getReadableDatabase();
}
public void add(InfoBean bean){
//sql:sql語(yǔ)句, bindArgs:sql語(yǔ)句中占位符的值
db.execSQL("insert into people(name,telephone) values(?,?);", new Object[]{bean.name,bean.telephone});
}
public void del(String name){
//sql:sql語(yǔ)句痛侍, bindArgs:sql語(yǔ)句中占位符的值
db.execSQL("delete from people where name=?;", new Object[]{name});
}
public void update(InfoBean bean){
//sql:sql語(yǔ)句朝氓, bindArgs:sql語(yǔ)句中占位符的值
db.execSQL("update people set telephone=? where name=?;", new Object[]{bean.telephone, bean.name});
}
public void query(String name){
//原始查詢 --> sql:sql語(yǔ)句, selectionArgs:查詢條件占位符的值,返回一個(gè)cursor對(duì)象
Cursor cursor = db.rawQuery("select _id, name, telephone from people where name = ?;", new String []{name});
//解析Cursor中的數(shù)據(jù)
if(cursor != null && cursor.getCount() >0){//判斷cursor中是否存在數(shù)據(jù)
//循環(huán)遍歷結(jié)果集主届,獲取每一行的內(nèi)容
while(cursor.moveToNext()){ //條件赵哲,游標(biāo)能否定位到下一行
//獲取數(shù)據(jù)
int id = cursor.getInt(cursor.getColumnIndex("_id"));
String name_str = cursor.getString(cursor.getColumnIndex("name"));
String phone = cursor.getString(cursor.getColumnIndex("telephone"));
Log.d("query result", "[ _id:"+id+" ,name:"+name_str+" ,phone:"+phone+" ]");
}
cursor.close();//關(guān)閉結(jié)果集
}
}
}
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
tools:context="com.example.databasedemo.MainActivity">
<Button
android:id="@+id/bt_add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add"/>
<Button
android:id="@+id/bt_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/update"/>
<Button
android:id="@+id/bt_del"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/del"/>
<Button
android:id="@+id/bt_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/query"/>
</LinearLayout>
MainActivity
package com.example.databasedemo;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import com.example.databasedemo.dao.InfoBean;
import com.example.databasedemo.dao.InfoDao;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Context mContext;
private InfoDao infoDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
// 創(chuàng)建數(shù)據(jù)庫(kù),有則打開(kāi)君丁,沒(méi)有則create
infoDao = new InfoDao(mContext, "test.db", 3);
Button btAdd = (Button) findViewById(R.id.bt_add);
Button btDel = (Button) findViewById(R.id.bt_del);
Button btUpdate = (Button) findViewById(R.id.bt_update);
Button btQuery = (Button) findViewById(R.id.bt_query);
btAdd.setOnClickListener(this);
btDel.setOnClickListener(this);
btUpdate.setOnClickListener(this);
btQuery.setOnClickListener(this);
}
@Override
public void onClick(View v) {
InfoBean bean = null;
InfoBean bean1 = null;
switch (v.getId()) {
case R.id.bt_add:
bean = new InfoBean();
bean.name = "張三";
bean.telephone = "119";
infoDao.add(bean);
bean1 = new InfoBean();
bean1.name = "李四";
bean1.telephone = "120";
infoDao.add(bean1);
break;
case R.id.bt_del:
infoDao.del("張三");
break;
case R.id.bt_update:
bean = new InfoBean();
bean.name = "張三";
bean.telephone = "110";
infoDao.update(bean);
break;
case R.id.bt_query:
infoDao.query("張三");
infoDao.query("李四");
break;
default:
break;
}
}
}
上面的方法基本是手寫sql語(yǔ)句枫夺,容易寫錯(cuò)。還有一種更便捷的方法 绘闷。
數(shù)據(jù)庫(kù)的CURD -2
使用db.insert()橡庞、db.delete()、db.update()印蔗、db.query()
package dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.example.databasedemo.MyDatabaseOpenHelper;
public class InfoDao {
private MyDatabaseOpenHelper myDatabaseOpenHelper;
private SQLiteDatabase db;
public InfoDao(Context context ,String dbName , int version){
//創(chuàng)建一個(gè)幫助類對(duì)象
myDatabaseOpenHelper = new MyDatabaseOpenHelper(context, dbName, null, version);
db = myDatabaseOpenHelper.getReadableDatabase();
}
public boolean add(InfoBean bean){
//執(zhí)行sql語(yǔ)句需要sqliteDatabase對(duì)象
ContentValues values = new ContentValues(); // 是用Map封裝的對(duì)象
values.put("name", bean.name);
values.put("telephone", bean.telephone);
// 第二個(gè)參數(shù)可以為空,返回值表示新增的行號(hào)扒最,-1表示添加失敗
long result = db.insert("people", null, values);
return result != -1;
}
public int del(String name){
//執(zhí)行sql語(yǔ)句需要sqliteDatabase對(duì)象
int count = db.delete("people", "name = ?", new String[]{name});
return count;
}
public int update(InfoBean bean){
ContentValues values = new ContentValues();
values.put("telephone", bean.telephone);
int count = db.update("people", values, "name = ?", new String[]{bean.name});
return count;
}
public void query(String name){
// 查詢people表中的name為參數(shù)指定的"_id", "name", "telephone"字段,按照id遞減
Cursor cursor = db.query("people", new String[]{"_id", "name", "telephone"}, "name = ?", new String[]{name}, null, null, "_id desc");
//解析Cursor中的數(shù)據(jù)
if(cursor != null && cursor.getCount() >0){//判斷cursor中是否存在數(shù)據(jù)
//循環(huán)遍歷結(jié)果集华嘹,獲取每一行的內(nèi)容
while(cursor.moveToNext()){ //條件吧趣,游標(biāo)能否定位到下一行
//獲取數(shù)據(jù)
int id = cursor.getInt(cursor.getColumnIndex("_id"));
String name_str = cursor.getString(cursor.getColumnIndex("name"));
String phone = cursor.getString(cursor.getColumnIndex("telephone"));
Log.d("query result", "[ _id:"+id+" ,name:"+name_str+" ,phone:"+phone+" ]");
}
cursor.close();//關(guān)閉結(jié)果集
}
}
}
@Override
public void onClick(View v) {
InfoBean bean = null;
int count = 0;
switch (v.getId()) {
case R.id.bt_add:
bean = new InfoBean();
bean.name = "張三";
bean.telephone = 119;
boolean result = infoDao.add(bean);
if (result) {
Toast.makeText(mContext, "添加成功", Toast.LENGTH_SHORT).show();
}
break;
case R.id.bt_del:
count = infoDao.del("張三");
Toast.makeText(mContext, "刪除"+count+"行", Toast.LENGTH_SHORT).show();
break;
case R.id.bt_update:
bean = new InfoBean();
bean.name = "張三";
bean.telephone = 110;
count = infoDao.update(bean);
Toast.makeText(mContext, "更新"+count+"行", Toast.LENGTH_SHORT).show();
break;
case R.id.bt_query:
infoDao.query("張三");
break;
default:
break;
}
}
使用以上方法不容易寫錯(cuò)sql語(yǔ)句,而且其返回值能方便地知道數(shù)據(jù)變化了幾條。
使用第二種方法更簡(jiǎn)單一些强挫,但是不能多表查詢(傳參時(shí)候只能傳入一個(gè)table)岔霸。而第一種手寫rawQuery()的方法可以實(shí)現(xiàn)「┎常可謂各有利弊呆细。
數(shù)據(jù)庫(kù)中的事務(wù)
執(zhí)行多條sql語(yǔ)句,要么同時(shí)執(zhí)行成功八匠,要么同時(shí)執(zhí)行失敗侦鹏。不能有的成功,有的失敗臀叙。失敗了則會(huì)回滾。
舉個(gè)銀行轉(zhuǎn)賬的例子价卤。因?yàn)楦鞣N原因比如在轉(zhuǎn)賬過(guò)程中突然斷電斷網(wǎng)劝萤,不能使得資金流失。李四個(gè)張三轉(zhuǎn)200慎璧,張三要么收到兩百床嫌。要么退還給李四(回滾到未轉(zhuǎn)錢的時(shí)候)。
還是需要一個(gè)繼承自SQLiteOpenHelper的類胸私,不過(guò)這次的比較簡(jiǎn)單了厌处。
package com.example.trancaction;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MyDatabaseOpenHelper extends SQLiteOpenHelper {
/**
* @param context 上下文
* @param name 數(shù)據(jù)庫(kù)的名稱
* @param factory 用來(lái)創(chuàng)建cursor對(duì)象,填入null使用默認(rèn)的
* @param version version:數(shù)據(jù)庫(kù)的版本號(hào)岁疼,從1開(kāi)始阔涉,如果發(fā)生改變,onUpgrade方法將會(huì)調(diào)用,4.0之后只能升不能降
*/
public MyDatabaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
// 直接在創(chuàng)建表的時(shí)候就添加數(shù)據(jù)
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table account (_id integer primary key autoincrement,name varchar(20),money varchar(20))");
db.execSQL("insert into account ('name','money') values ('張三','2000')");
db.execSQL("insert into account ('name','money') values ('李四','5000')");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO: 2017/4/11
}
}
package com.example.trancaction;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
Button btTransfer = (Button) findViewById(R.id.bt_transfer);
btTransfer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyDatabaseOpenHelper dbHelper = new MyDatabaseOpenHelper(mContext, "account.db", null, 1);
SQLiteDatabase db = dbHelper.getReadableDatabase();
//3.轉(zhuǎn)賬,將李四的錢減200捷绒,張三加200
db.beginTransaction();//開(kāi)啟一個(gè)數(shù)據(jù)庫(kù)事務(wù)
try {
// 如果沒(méi)有事務(wù)瑰排,這里只會(huì)執(zhí)行李四的錢轉(zhuǎn)出,張三收不到暖侨。
db.execSQL("update account set money= money-200 where name=?", new String[]{"李四"});
int i = 100 / 0;//模擬一個(gè)異常
db.execSQL("update account set money= money+200 where name=?", new String[]{"張三"});
db.setTransactionSuccessful();//能運(yùn)行到最后這兒椭住,就標(biāo)記事務(wù)中的sql語(yǔ)句全部成功執(zhí)行
} finally {
db.endTransaction();//判斷事務(wù)的標(biāo)記是否成功,如果不成功字逗,回滾錯(cuò)誤之前執(zhí)行的sql語(yǔ)句
}
}
});
}
}
by @sunhaiyu
2017.4.13