Android入门笔记 - 数据存储 - SQLite,单元测试

今天我们来接触一个轻轻轻量级数据库(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的属性值来存储的,而不是单个的值。数据库中的每一行存储一个对象的所有属性,当然也可能是几张表联合。


你可能感兴趣的:(android,sqlite,单元测试)