今天我们来接触一个轻轻轻量级数据库(SQLite),为什么要加3个轻呢?因为它确实很轻。 Sqlite是专门未嵌入式设备准备的轻量级数据库,麻雀虽小,五脏俱全,sqlite的功能却一点都不少。它和其他的数据库:MySql,SqlServer,Oracle等数据库的最大区别我觉得就是Sqlite只能运行在终端,不能用在服务器上,这也体现了它为嵌入式设备工作的初衷。
好了,来看看今天的内容:
- SQLite
- Android单元测试
今天我们不仅要介绍sqlite,还要简单介绍一下怎么使用android的单元测试。如果不知道什么是单元测试,那就去找google吧!
在android中使用 sqlite数据库,一般要通过一个协助类: SQLiteOpenHelper,这个类中有两个方法:onCreate() ,和 onUpgrade()。
- onCreate() 在数据库第一次被创建时才调用。
- onUpgrade()当数据库版本更新时调用。
是什么意思呢?来看看代码:
package db;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteDatabase.CursorFactory;import android.database.sqlite.SQLiteOpenHelper;import android.transition.Transition;import android.util.Log;public class DBOpenHelper extends SQLiteOpenHelper { private static final String TAG = "DBOpenHelper"; private Context mContext; private String mDBname; private String mTableName = "students"; private SQLiteDatabase mDatabase; private int mVersion; private String CREATE_TABLE = "create table if not exists " + mTableName + " (_id integer primary key, name text)"; public DBOpenHelper(Context context, String dbname, CursorFactory factory, int version) { super(context, dbname, factory, version); this.mContext = context; this.mDBname = dbname; this.mVersion = version; } @Override public void onCreate(SQLiteDatabase db) { Log.e(TAG, "onCreate"); db.execSQL(CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.e(TAG, "onUpgrade"); } public long insert(String data) { if (mDatabase == null) { mDatabase = getWritableDatabase(); } ContentValues values = new ContentValues(); values.put("name", data); return mDatabase.insert(mTableName, "_id", values); } public int delete(int id) { if (mDatabase == null) { mDatabase = getWritableDatabase(); } return mDatabase.delete(mTableName, "_id" + "=" + id, null); } public int update(int id, String data) { if (mDatabase == null) { mDatabase = getWritableDatabase(); } ContentValues values = new ContentValues(); values.put("name", data); return mDatabase.update(mTableName, values, "_id" + "=" + id, null); } public Cursor getItemAt(int id) { if (mDatabase == null) { mDatabase = getWritableDatabase(); } Cursor cursor = mDatabase.query(mTableName, new String[] { "_id", "name" }, "_id" + "=" + id, null, null, null, null, null); if (cursor != null) { cursor.moveToFirst(); } return cursor; } public Cursor getAll() { if (mDatabase == null) { mDatabase = getWritableDatabase(); } Cursor cursor = mDatabase.query(mTableName, new String[] { "_id", "name" }, null, null, null, null, null); return cursor; }}在使用SqliteOpenHelper时,这三个方法是必须的:
public DBOpenHelper(Context context, String dbname, CursorFactory factory, int version) { super(context, dbname, factory, version); this.mContext = context; this.mDBname = dbname; this.mVersion = version; } @Override public void onCreate(SQLiteDatabase db) { Log.e(TAG, "onCreate"); db.execSQL(CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.e(TAG, "onUpgrade"); }构造方法完成 Context, 数据库名称,数据库版本 的初始化,CursorFactory一般用不到,直接穿为null。
在onCreate()我们调用 db.execSQl( CREATE_TABLE) ; 初始化了一张表 “students”,那么这个DBOpenHelper类也就是为这张表工作了。
至于 onUpdrade()是在调用 构造函数时,version的值 大于 上一个值的时候就会被触发,然后 onUpgrade()中主要做表的数据修改列啊,备份数据等工作,这里不讲。
然后我们来看看数据库的增删改查是怎么完成的:
增:
使用android提供的ContentValues 可以轻易的初始化要添加的对象,它相当于一个键值对,如果有多项,依次用put方法添加就可以了,注意key一定要与数据库中的列对应。
这里的 mDatabase 是一个SqliteDatabase 对象,专门用来操作数据库的,可通过 SqliteOpenHelper. getWritableDatabase()获得,然后通过调用 insert()方法就可以插入数据。这里大家需要注意,使用sqlite时,表中必须有一个字段叫 “_id",而且是主键自增长,这就是insert的第二个参数的值。
相当于执行 sql语句:
insert into students(_id, name) values(id, name);public long insert(String data) { if (mDatabase == null) { mDatabase = getWritableDatabase(); } ContentValues values = new ContentValues(); values.put("name", data); return mDatabase.insert(mTableName, "_id", values); }
删:
第二个参数是字符串,相当于: "where _id=id " 只是 delete方法自己做了拼接。
相当于执行 sql语句:
delete from students where _id=id;
public int delete(int id) { if (mDatabase == null) { mDatabase = getWritableDatabase(); } return mDatabase.delete(mTableName, "_id" + "=" + id, null); }
改:
update方法就需要提供 条件, 以及修改的值,对应于 values 和 ”_id"=id 两个参数。
相当于执行 sql语句:
update students where _id=id set name = "data";
public int update(int id, String data) { if (mDatabase == null) { mDatabase = getWritableDatabase(); } ContentValues values = new ContentValues(); values.put("name", data); return mDatabase.update(mTableName, values, "_id" + "=" + id, null); }
查:
查找需要注意的是android中使用到了游标作为结果集(java中的ResultSet),如果找到,返回到游标在第一行数据的前面,所以需要先调用一次 cursor.moveToFirst() 或者 cursor.moveToNext() ,这样就可以指向第一行数据。
public Cursor getItemAt(int id) { if (mDatabase == null) { mDatabase = getWritableDatabase(); } Cursor cursor = mDatabase.query(mTableName, new String[] { "_id", "name" }, "_id" + "=" + id, null, null, null, null, null); if (cursor != null) { cursor.moveToFirst(); } return cursor; } public Cursor getAll() { if (mDatabase == null) { mDatabase = getWritableDatabase(); } Cursor cursor = mDatabase.query(mTableName, new String[] { "_id", "name" }, null, null, null, null, null); return cursor; }
好了,sqlite的操作看完了,我们来看看怎么使用 DBOpenHelper这个类。这个时候我们使用到了Android的单元测试。
Android单元测试的配置方法:
1. 在AndroidMainefest.xml 文件中添加注册。
<application> 标签之间添加: <uses-library android:name="android.test.runner"/>
<manifest> 根标签下添加: <instrumentation android:targetPackage="com.example.ch6_04_sqlite"
android:name="android.test.InstrumentationTestRunner"></instrumentation> (注意加粗的部分填写程序第一个Activity启动的所在包名)
2. 测试类必须继承与 AndroidTestCase。
3. 每个测试方法都以 test+要测试的方法 名作为测试方法名, 并且需要抛出异常。
我们来看看代码:
package Test;import db.DBOpenHelper;import android.database.Cursor;import android.test.AndroidTestCase;import android.util.Log;public class TestDBOpenHelper extends AndroidTestCase { private static final String TAG = "TestDBOpenHelper"; public void TestInsert() throws Throwable{ Log.e(TAG,"TestInsert"); DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1); dbopenhelper.insert("stu1"); dbopenhelper.insert("stu2"); dbopenhelper.insert("stu3"); dbopenhelper.insert("stu4"); dbopenhelper.insert("stu5"); } public void TestDelete(){ DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1); dbopenhelper.delete(1); dbopenhelper.delete(2); } public void TestUpdate(){ DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1); dbopenhelper.update(3, "333"); dbopenhelper.update(4, "444"); } public void TestGetItemAtId(){ Log.e(TAG,"TestGetItemAtId"); DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1); Cursor cursor = dbopenhelper.getItemAt(3); Log.e(TAG,"_id="+cursor.getInt(0)); Log.e(TAG,"name="+cursor.getString(1)); } public void TestGetAll(){ Log.e(TAG,"TestGetAll"); DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1); Cursor cursor = dbopenhelper.getAll(); while(cursor.moveToNext()){ Log.e(TAG,"_id="+cursor.getInt(0)); Log.e(TAG,"name="+cursor.getString(1)); } }}
写好之后我们就可以启动单元测试来测试我们写的代码:
如果正确就会绿条,有异常会显示红条:
单元测试的好处多多,关心代码质量的同志们都应该学会怎么使用单元测试,它是为了验证每个代码块都能工作正常而产生的。
测试代码应该很容易就看懂了:
这是在测试添加数据,只需要new一个 helper对象,然后调用insert方法就可以了,是不是很简单:
测试插入:
public void TestInsert() throws Throwable{ Log.e(TAG,"TestInsert"); DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1); dbopenhelper.insert("stu1"); dbopenhelper.insert("stu2"); dbopenhelper.insert("stu3"); dbopenhelper.insert("stu4"); dbopenhelper.insert("stu5"); }
测试删除:
public void TestDelete(){ DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1); dbopenhelper.delete(1); dbopenhelper.delete(2); }
测试查找:
public void TestGetAll(){ Log.e(TAG,"TestGetAll"); DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1); Cursor cursor = dbopenhelper.getAll(); while(cursor.moveToNext()){ Log.e(TAG,"_id="+cursor.getInt(0)); Log.e(TAG,"name="+cursor.getString(1)); } }刚刚我们看到,getAll() 函数返回到是一个 cursor,我们可以通过循环遍历每个cursor中的对象。 得到cursor某一列的值 可通过 cursor.getXXX( index ) 来获取,index是column的编号,从0开始,当然也可以通过列的名字来得到。总的来说 Cursor的一行就像一个Map一样,存放数据库中的一行数据的键值对。
好了,终于完了,最后还是要说一下,上面的例子是很不规范的,当然只是为了体现应该怎么来使用Sqlite存取数据,在真实的开发中,我们传入的内容应该是 一个对象,这里需要和JavaBean结合,就是说数据库中存储的数据,其实是针对一个Class的属性值来存储的,而不是单个的值。数据库中的每一行存储一个对象的所有属性,当然也可能是几张表联合。