5.1 创建数据访问对象类
数据访问对象即Data Access Object(缩写为 DAO )。它向需要访问数据的模块提供接口调用,而隐藏数据库操作的具体细节。
虽然在UI代码中插入数据库操作代码并不会引起语法错误,但是从软件架构设计的角度来考虑,进行层次化的设计,更有利于使软件结构清晰,便于阅读、理解和维护。
步骤1:在我们应用程序的包下创建名为dao的子包:
![](https://img.haomeiwen.com/i10901316/7f094c955f88cff2.png)
步骤2:在dao包中创建名为NoteDAO数据访问对象类:
package com.jing.app.sn.dao;
public class NoteDAO {
}
步骤3:为NoteDAO类添加一个Context类型属性——后面的数据库操作需要这个对象:
public class NoteDAO {
private Context context;
}
步骤4:将NoteDAO类改写成单例模式
按照之前学习过的单例模式实现方法,将NoteDAO类设置为单例:
public class NoteDAO {
/**
* 采用单例模式
*/
private static NoteDAO sInstance;
public static NoteDAO getInstance(Context context) {
if (sInstance == null) {
sInstance = new NoteDAO(context);
}
return sInstance;
}
private Context context;
/**
* 私有构造方法
* @param context
*/
private NoteDAO(Context context) {
this.context = context;
}
}
可以注意到,这次单例模式的实现中,稍有不同的是getInstance()方法多了一个Context类型的参数,这在后面的编码中要用到。
5.2 创建SQLiteOpenHelper子类
在SQLiteOpenHelper类的基础上扩展(extends)我们自己的子类NoteDbHelper,通过它来创建、维护和获取我们的数据库对象。
从软件设计的角度考虑,我们没有必要在NoteDAO类之外的地方使用SQLiteOpenHelper对象。因此考虑将NoteDbHelper类定义为NoteDAO类的非公有静态内部类。
在NoteDAO类的内部创建静态(static)类NoteDbHelper并继承SQLiteOpenHelper类,然后添加必要构造方法并重写两个回调方法:
public class NoteDAO {
...
static class NoteDbHelper extends SQLiteOpenHelper {
public NoteDbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
对于NoteDbHelper类的构造方法,我们主要关注它的两个参数:
- name: 数据库文件名。我们自定一个文件名提供给构造方法。
- version: 数据库版本。如果在开发过程中要对数据库进行修改,如新建、删除数据表或修改数据表结构,则应提供大于当前版本的数值。
为NoteDAO类添加NoteDbHelper成员变量
NoteDAO类需要持有一个NoteDbHelper类对象,以便通过它来访问数据库。同时,要在NoteDAO类的构造方法中对其分配内存并设置参数。
找到NoteDAO类的构造方法,将其改写为如下形式:
private NoteDAO(Context context) {
this.context = context;
dbHelper = new NoteDbHelper(context, "note.db", null, 1);
}
当创建NoteDbHelper类对象时,构造方法被调用。此时:
- 如果name参数指定的数据库文件不存在,就创建这个文件,并且调用onCreate()回调方法;
- 如果version参数给出的版本号大于当前版本号,则调用onUpgrade()回调方法;
至于两个回调方法具体做什么,要由我们自己来定义。下面我们重点关注onCreate()回调方法。
5.3 创建note表
此时,在onCreate()中,我们只需要将note表创建起来。根据数据库设计,需要执行以下的SQL语句:
CREATE TABLE note (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
content TEXT,
createTime INTEGER
)
理论上,我们可以直接将这个SQL语句作为字符串参数,传递给SQLiteDatabase类的execSQL()方法来执行。但是,其中涉及到的表名("note")和列名("id","title","content","createTime")除了用在这里,在之后的所有数据库访问操作中也仍然会经常使用。比较方便的做法是将它们定义为常量,这样便于引用。同样的道理,我们将数据库文件名称、版本号也定义为常量,以便统一管理维护。
创建常量文件
在包dao中创建名为Constants.java的源文件作为常量存放位置:
![](https://img.haomeiwen.com/i10901316/de43af90e304da46.png)
打开Constants.java,在其中定义各个常量:
public class Constants {
// 数据库文件名
public static final String DB_NAME = "note.db";
// 数据库版本号
public static final int VERSION = 1;
// note表名字
public static final String TABLE_NOTE = "note";
// note表各列名字
public static final String COL_ID = "_id";
public static final String COL_TITLE = "title";
public static final String COL_CONTENT = "content";
public static final String COL_CREATE_TIME = "createTime";
}
于是,上面的建表SQL语句可以改写为:
"CREATE TABLE " + TABLE_NOTE + "(" +
COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COL_TITLE + " TEXT, " +
COL_CONTENT + " TEXT, " +
COL_CREATE_TIME + " INTEGER)"
接下来,回到NoteDAO.java文件,在文件上部众多的"import ......"语句之后,添加如下语句以将这些常量导入(xxx.xxx.xx部分替换成你自己的应用程序包名):
import static xxx.xxx.xxx.dao.Constants.*;
这样就可以直接引用这些常量了。
用常量数据库名和版本号替换硬编码(hard code)
找到NoteDAO类的构造方法。在前面,我们已经在这里创建了NoteDbHelper类的实例:
private NoteDAO(Context context) {
this.context = context;
dbHelper = new NoteDbHelper(context, "note.db", null, 1);
}
用刚才定义的常量替换数据库文件和版本两个参数,变成如下形式:
private NoteDAO(Context context) {
this.context = context;
dbHelper = new NoteDbHelper(context, DB_NAME, null, VERSION);
}
编写创建note表代码
回到NoteDbHelper类的onCreate()方法,添加创建note表相关的代码:
db.execSQL("CREATE TABLE " + TABLE_NOTE + "(" +
COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COL_TITLE + " TEXT, " +
COL_CONTENT + " TEXT, " +
COL_CREATE_TIME + " INTEGER)");
编写单元测试用例
由于我们目前还没有将数据库与应用程序主体部分对接,因此很难立即对刚才编写的数据库模块进行验证。可以运用Android的单元测试框架来解决这个问题。
首先我们为了测试方便,在NoteDAO类的构造函数下面增加如下的方法:
NoteDbHelper getDbHelper() {
return dbHelper;
}
这个方法让同在dao包下的某个类可以获取到dbHelper对象。
接下来,找到名为androidTest的目录:
![](https://img.haomeiwen.com/i10901316/70b200eb940519a4.png)
可以看到,androidTest下有一个和我们应用程序同名的包。在这个包下同样创建一个名为dao的子包,并在里面添加测试类NoteDAOTest:
![](https://img.haomeiwen.com/i10901316/c8bb7f720a18176f.png)
为NoteDAOTest类编写测试代码如下:
@RunWith(AndroidJUnit4.class)
public class NoteDAOTest {
@Test
public void testCreateDatabase() throws Exception {
Context context = InstrumentationRegistry.getTargetContext();
NoteDAO dao = NoteDAO.getInstance(context);
NoteDAO.NoteDbHelper dbHelper = dao.getDbHelper();
// 取只读的数据库对象
SQLiteDatabase db = dbHelper.getReadableDatabase();
// 检查数据库对象是否获取成功
assertNotNull(db);
// 对note表进行一次查询
Cursor cursor = db.query(Constants.TABLE_NOTE, null, null, null, null, null, null);
// 检查查询操作是否成功
assertNotNull(cursor);
}
}
然后右键单击NoteDAOTest类,在菜单中选择“Run 'NoteDAOTest'”并执行。如果测试通过,在Android Studio窗口下方将显示如下的信息:
![](https://img.haomeiwen.com/i10901316/1bab5933ec0bea47.png)
整个单元测试执行流程如下:
![](https://img.haomeiwen.com/i10901316/4e4a10823e780f19.gif)
网友评论