SQLite数据存储
这里需要说明的是,要使用这种存储,在开发的时候最好是使用模拟器或者把真机root,不然没有权限访问数据库所在的位置、po主直接把三星的s4用刷机大师刷成了别的系统,顺便root掉了。
sqlite是轻量级的数据库,安卓内置的,不需要账户密码就可以使用,对于数据量大,结构复杂的数据,使用ShareedPreference都不太方便。
当然,sqlite有自己特定的语法和数据类型,例如real(浮点),text(文本)等,使用sqlite需要一个帮助类:
SQLiteOpenHelper
这个类是个抽象类,需要我们自己创建一个类去实现它并且重写相关的方法,一个是onCreate()
,一个是onUpgrade()
还有两个重要的方法,用于打开数据库并返回一个SQLiteDatabase
,数据库的CRUD操作需要通过这个SQLiteDatabase
对象进行,如果存在直接打开否则创建一个新的数据库。
1、getReadableDatabase()
2、getWritebleDatabase()//当数据库不可写入的时候使用这个方法会抛异常
一般新建表的操作写在onCreate()
里面,例如:
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
Toast.makeText(mContext,"创建成功",Toast.LENGTH_SHORT).show();
}
使用时,myDatabaseHelper = new MyDatabaseHelper(this,"book.db",null,1);
就可以得到一个MyDatabaseHelper
对象,MyDatabaseHelper
的构造方法中有四个参数,第一个是Context对象,第二个是数据库的名字,最后一个是版本号,如果数据库名不变的话,onCreate()
只会执行一次,此时只有通过改变版本号,使其回调onUpgrade()
方法,才能对这个数据库进行升级,例如要本来数据库里有一张book表,要新添加一张category表,则可以:
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext,"创建成功",Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
之前的版本号参数改成比1大的数就可以触发onUpgrade()
方法。
如果想知道数据库是否创建成功,需要进入/data/data/com.jackie.databasetest/databases
查看
D:\DevelopKit\Android\AndroidProjects\DatabaseTest>adb root
adbd is already running as root
D:\DevelopKit\Android\AndroidProjects\DatabaseTest>adb shell
root@ja3g:/data/data/com.jackie.databasetest/databases # ll
-rw-rw---- u0_a64 u0_a64 24576 2015-11-11 21:42 book.db
-rw-rw---- u0_a64 u0_a64 8720 2015-11-11 21:42 book.db-journal
可以看到有存在book.db文件
查看它需要sqlite3中的相关指令,.table可以查看表,.schema可以查看表结构。。。
root@ja3g:/data/data/com.jackie.databasetest/databases # sqlite3 book.db
SQLite version 3.7.11 2012-03-20 11:35:50
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .table
Category android_metadata book
可以看到这里有两个表,就是之前新建的,不过此时都是空表,里面没有数据。
CRUD操作,是数据库的核心,也是开发应用的业务主体
添加数据(Create)
添加一个按钮用于添加数据
直觉告诉我们,添加数据应该是通过SQLiteDatabase
的方法实现
insert(String table, String nullColumnHack, ContentValues values)
可以看到,这个方法第一个参数很容易想到是操作的表名,第二个不用管,第三个参数是一个ContentValues
对象,其实这个对象就相当于一条数据,通过key-value的形式为key字段赋值,例如要添加两本书,如果要重复使用同一个ContentValues
对象,则要记得插入后clear一下再重新put数据
case R.id.btnAdd:
SQLiteDatabase db = myDatabaseHelper.getWritableDatabase();
ContentValues book = new ContentValues();
book.put("name","无声告白");
book.put("price",17.15);
book.put("pages",290);
book.put("author", "伍绮诗");
db.insert("book", null, book);
book.clear();
book.put("name","年少荒唐");
book.put("price",18.00);
book.put("pages",323);
book.put("author", "朱炫");
db.insert("book", null, book);
break;```
sqlite> select * from book;
1|伍绮诗|17.15|290|无声告白
2|朱炫|18.0|323|年少荒唐
可以看到book表中已经有了刚刚添加的两本书
删除数据(Delete)
同样看看SQLiteDatabase
中的方法
public int delete(String table, String whereClause, String[] whereArgs) {
acquireReference();
try {
SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
(!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
try {
return statement.executeUpdateDelete();
} finally {
statement.close();
}
} finally {
releaseReference();
}
}
可以看到,第一个参数仍然是表名,第二个参数是条件,第三个参数是条件的边界
那么我要删除掉id为3的数据
可以这么写
case R.id.btnDel:
db.delete("book","id=?",new String[]{"3"});
break;
更改数据(Update)
/**
* Convenience method for updating rows in the database.
*
* @param table the table to update in
* @param values a map from column names to new column values. null is a
* valid value that will be translated to NULL.
* @param whereClause the optional WHERE clause to apply when updating.
* Passing null will update all rows.
* @param whereArgs You may include ?s in the where clause, which
* will be replaced by the values from whereArgs. The values
* will be bound as Strings.
* @return the number of rows affected
*/
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
}
可以看到我们需要一个ContentValues
对象,和一个约束条件(更新哪一条数据)
例如更新id为1的书的价格为20.00
case R.id.btnUp:
ContentValues update = new ContentValues();
update.put("price",20.00);
db.update("book",update,"id=?",new String[]{"1"});
sqlite> select * from book;
1|伍绮诗|20.0|290|无声告白
2|朱炫|18.0|323|年少荒唐
查询数据(Retrieve)
查询数据的方法,返回的是一个Cursor,通过遍历Cursor就可以得到每一条数据的内容。
这里是通过
Cursor query(
String table,
String[] columns,
String selection,
String[] selectionArgs,
String groupBy,
String having,
String orderBy)
来查询,各个参数的含义比较明显可以看出来,为了这里我们进行全表查询
case R.id.btnRe:
Log.v("jackie","查询数据");
Cursor cursor = db.query("book",null,null,null,null,null,null,null);
//遍历Cursor,用Log打印数据
if(cursor.moveToFirst()){
do{
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
Log.v("jackie", "name is" + " " + name);
Log.v("jackie", "author is" + " " + author);
Log.v("jackie", "price is" + " " + price);
Log.v("jackie", "pages is" + " " + pages);
}while(cursor.moveToNext());
}
break;
11-11 23:19:20.176 12846-12846/? V/jackie: 查询数据
11-11 23:19:20.181 12846-12846/? V/jackie: name is 无声告白
11-11 23:19:20.181 12846-12846/? V/jackie: author is 伍绮诗
11-11 23:19:20.181 12846-12846/? V/jackie: price is 20.0
11-11 23:19:20.181 12846-12846/? V/jackie: pages is 290
11-11 23:19:20.181 12846-12846/? V/jackie: name is 年少荒唐
11-11 23:19:20.181 12846-12846/? V/jackie: author is 朱炫
11-11 23:19:20.181 12846-12846/? V/jackie: price is 18.0
11-11 23:19:20.181 12846-12846/? V/jackie: pages is 323