如果是为了创建一个简单数据库的话,这种方法比简单的逐行执行SQL语句来的复杂的多,但是一旦数据库结果变得更复杂或者你想预先写好创建脚本时,这种方法将会使你获益良多。同时你也看到我将执行SQL语句的代码抽象到了一个独立executeSQLScript方法中,这样它可以在其他情况下复用,这一点我在本文后面的代码会证实到的。

  (【译者注】这里原作者没有说创建了DB类以后该如何使用DB类来创建数据库,但既然这是个入门级的说明文,应该假设读者没有使用过SQLite,在下一小节的代码中有这样的代码:

  DB db = new DB(this);

  SQLiteDatabase qdb = db.getReadableDatabase();

  也可以使用getWritableDatabase(); 得到可以写入的数据库,这里Android会自己根据需要创建数据库,如果数据库文件不存在的话创建,如果数据库文件已经存在的话,那么Android自己会根据数据库文件中的version信息和DB类构造函数中传入的Version信息对比,如果值不一样的话,会自己调用onUpgrade()方法更新数据库。因此onCreate方法和onUpgrade方法都不是由用户手动调用的。

  这两句话可以加在Activity的onCreate方法中,或者onResume方法也是个不错的选择,执行完这句话后,在Android系统的/data/data/package_name/databases目录下可以看到你创建的数据库文件,文件名是上面代码中的DB_NAME的值,用DDMS拿出来以后可以用图形化工具打开看看,图形化工具可以使用有免费开源的sqliteman,在http://sqliteman.com/page/4.html下载;另外还有firefox的插件:sqlite manager可以使用)

  与数据库交互

  现在数据库已经创建好了,下面我想和它进行交互。一个简单的操作步骤:

  第一步是打开数据库,有两种方法可以做到这点:使用getReadableDatabase()方法或者getWritableDatabase()方法。前者速度快,占用资源少,可以用来做除了写数据库和修改数据库之外的所有操作;后者主要用来做insert, update等操作。

  在Android系统中,查询结果集作为一个Cursor对象返回,可以调用query()或者rawQuery()方法执行一次查询,例如,下面的两个方法返回的结果完全相同:
 
DB db = new DB(this);
SQLiteDatabase qdb = db.getReadableDatabase();
Cursor recordset1 = 
qdb.query("mytable", null, null, null, null, null, null);
Cursor recordset2 = qdb.rawQuery("SELECT * FROM mytable", null);

  第一个查询调用使用了一堆参数,他们分别是数据表名称,一个列名数组, WHERE子句,选择参数数组,GROUP BY子句,HAVING子句以及ORDER BY子句。可以注意到,把这么多参数都设置为null,其作用和你用通配符代替这些参数的效果是一样的,如果你不需要给这些参数赋值,你干脆不要用这种包含那么多参数的方法。

  这里大多数参数对于熟悉SQL语句的人来说相当的直白。不过这个选择参数数组需要一点点说明,它是一个字符串数组,在查询方法中,WHERE子句中可以包含‘?’,然后在查询时,所有问号依次被选择参数数组中的值替换,比如选择参数数组中的第一个值替换掉WHERE子句中第一个‘?’。

  再看看rawQuery()方法,它只需要两个参数,第一个是SQL查询语句,第二个是选择参数数组-它的作用和query方法中的一样。选择参数数组一般和复杂的查询一起使用,比如说使用到JOIN操作的时候。(【译者注】这里原作者说到做连接操作,我们知道sqlite仅支持左连接,而且当left join时,连接条件不在where子句中,因此这里应该指select x1, x2 from tables1, tables 2 where table1.?=tables2.? 这样的连接操作。实际上,我觉得这样的设计应该是为了不需要每次查询都要拼接查询语句字符串,比如说写个select * from tablename where name = ?, 这个?每次查询都不一样,这样可以通过选择参数数组中的值来替换?,而不需要每次都”select * from tablename where name = ‘“+Michael+”’” 这样拼接字符串,Java中拼接字符串的代价是比较高的,特别是查询条件比较多的时候,连续的几次字符串+操作会创建一堆String对象)。

  数据库更新(Upgrades)

  再回到数据库管理上来,让我们看一下有点小复杂的情况,数据库更新。经过一段时间的使用和开发,应用程序往往会发生变化,也许会添加新的功能,也许做了某些优化。这些变化也许需要数据库结构发生变化,并且在数据库更新代码中,通过数据库版本这个值来反映出这次更新。

  在更新数据库时有个潜在问题,那是有可能导致先前版本的数据库数据丢失。另外,一旦我们的应用版本超过了两个,我们不能武断的假设用户总是已经更新到新的版本了,比如说现在发布的版本是version3.0,那么不能假设所有用户都已经更新到version2.0了,所以我们的更新操作不能是简单的从一个版本更新到下一本版本。

  那么怎么处理这种问题呢?我们已经知道当有一个新版本数据库的时候,onUpgrade()方法会被调用,所以理想的做法是我们在这个方法中判断数据库版本,决定执行一段或者多段更新脚本。

  让我们看一下我们的例子中,在2.0版本中要做哪些修改:

  电话号码格式标准化(分机号,手机号),将电话号码存到一张独立的“numbers”数据表。

  在雇员数据表中增加一个薪水字段。

  以版本1数据库为更新点,可以通过下面的SQL脚本实现更新数据库操作,并且将原来版本中的数据导入到新版本数据库中。

CREATE TABLE numbers (
   _id INTEGER PRIMARY KEY AUTOINCREMENT,
   employid INTEGER NOT NULL,
   number TEXT NOT NULL,
   ntype INTEGER NOT NULL DEFAULT '0'
);
CREATE INDEX employid ON numbers(employid);
 
INSERT INTO numbers (employid, number, ntype) SELECT _id, ext, 0
   FROM employees;
INSERT INTO numbers (employid, number, ntype) SELECT _id, mob, 1
   FROM employees;
 
CREATE TABLE temp (
     _id INTEGER PRIMARY KEY AUTOINCREMENT,
     name TEXT NOT NULL,
     salary INTEGER NOT NULL DEFAULT '0'
);
INSERT INTO temp (_id, name) SELECT _id, name FROM employees;
 
DROP TABLE employees;
ALTER TABLE temp RENAME TO employees;