都知道在Android中SQLiteOpenHelper是用来创建和升级数据库,参考
$ANDROID_SDK_HOME/docs/reference/android/database/sqlite/SQLiteOpenHelper.html
软件发布出去了,用户已经安装使用了,但是随着软件的升级,数据库结构做了些改动,我们不希望用户把应用卸载了再装(这样会丢失应用所有的数据),我们希望在数据库总体结构和已有数据不变的情况下做些小的改动,比如新增一个字段或索引,新增加一个表等等,那么这个时候我们就要用到这个类了
常用的也就是onCreate和onUpgrade这两个方法,在使用的时候这两个方法都需要重写,里面实现自己的逻辑
我们先列出一个场景:
假设第一版程序发布出去,First Public Version,代码如下
1 | @Override |
2 | public void onCreate(SQLiteDatabase db) { |
3 | ???? bootstrapDB(db); |
4 | } |
5 | ? |
6 | @Override |
7 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { |
8 | } |
onUpgrade里面没有代码,第一版出去没有需要更新的,bootstrapDB方法就是些DDL和数据初始化操作等等
之后过了一段时间,新的程序发布(其中数据库结构做了些变化),这个时候已经开始使用第一版程序的用户就需要升级,我们不希望他已经存在的数据被破坏,那么我们发布出去的新的版本中代码该怎么写呢?
直接看代码,这些代码都是从Android自带的应用中抽取出来的,做了些具体业务上的简化,主要是阐述清楚用法
1 | @Override |
2 | public void onCreate(SQLiteDatabase db) { |
3 | ???? bootstrapDB(db); // 这个方法里面都是最新版的初始化方法 |
4 | } |
5 | ? |
6 | @Override |
7 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { |
8 | ???? Log.i(TAG, "Upgrading DB from version " + oldVersion + " to " |
9 | ???????????? + newVersion); |
10 | ???? if (oldVersion == 1 ) { |
11 | ???????? upgradeToVersion2(db); |
12 | ???????? oldVersion += 1 ; |
13 | ???? } |
14 | ???? Log.v( "do upgrade" , "我更新了。。。" ); |
15 | } |
这样如果后来又有新的程序发布,那么这两个方法会变成类似这个样子
1 | @Override |
2 | public void onCreate(SQLiteDatabase db) { |
3 | ???? bootstrapDB(db); // 这个方法里面始终都是最新版的初始化方法 |
4 | } |
5 | ? |
6 | @Override |
7 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { |
8 | ???? Log.i(TAG, "Upgrading DB from version " + oldVersion + " to " |
9 | ???????????? + newVersion); |
10 | ???? if (oldVersion == 1 ) { |
11 | ???????? upgradeToVersion2(db); |
12 | ???????? oldVersion += 1 ; |
13 | ???? } |
14 | ???? if (oldVersion == 2 ) { |
15 | ???????? upgradeToVersion3(db); |
16 | ???????? oldVersion += 1 ; |
17 | ???? } |
18 | ???? // 这是一种逐级更新的方式 |
19 | ???????????? // 对于目前使用的还是第一版的用户而言,会先执行完upgradeToVersion2再执行upgradeToVersion3 |
20 | ???????????? // 对于目前使用的还是第二版的用户而言,会执行upgradeToVersion3 |
21 | ???? Log.v( "do upgrade" , "我更新了。。。" ); |
22 | } |
这样也许就能看的很清楚这个类的意图和用法了,后面版本一直增加的话,我们就一直这样写就好,保证全新的用户和升级的用户都能正常使用,那么我们如何来调用呢
一般我们会有个构造方法,有个参数就是数据库的版本,比如下面这两个构造方法
1 | public MyDatabaseHelper(Context context, String name, CursorFactory factory, |
2 | ???????? int version) { |
3 | ???? super (context, name, factory, version); |
4 | } |
5 | ? |
6 | public MyDatabaseHelper(Context context, int version) { |
7 | ???? super (context, NAME, null , version); |
8 | } |
采用如下方式调用
1 | helper = new MyDatabaseHelper(context, 10 ); // 这个数据库版本号会随着程序的每次发布而变化,是表示每次需要更新到的版本号,也就是最新的版本号 |
2 | sqlite = helper.getWritableDatabase(); |
其实更好理解这个用法就是读SQLiteOpenHelper.getWritableDatabase这个方法,里面有段代码
1 | int version = db.getVersion(); |
2 | if (version != mNewVersion) { |
3 | ???? db.beginTransaction(); |
4 | ???? try { |
5 | ???????? if (version == 0 ) { |
6 | ???????????? onCreate(db); |
7 | ???????? } else { |
8 | ???????????? onUpgrade(db, version, mNewVersion); |
9 | ???????? } |
10 | ???????? db.setVersion(mNewVersion); |
11 | ???????? db.setTransactionSuccessful(); |
12 | ???? } finally { |
13 | ???????? db.endTransaction(); |
14 | ???? } |
15 | } |
另外,看看set/get Version就知道数据库版本标记是通过PRAGMA user_version;这个命令来完成的,你也可以用sqlite3之类的工具把数据库文件打开,然后执行PRAGMA user_version查看或者设置版本值
如下是完整的两个代码,是目前在使用的
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 | package org.xkit.android.demo; ? import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; ? import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; ? public class DBAdapter { private Context context; private SQLiteDatabase sqlite; ? private MyDatabaseHelper helper; ? public DBAdapter(Context c) { this.context = c; } ? public void open() { helper = new MyDatabaseHelper(context, 10); sqlite = helper.getWritableDatabase(); } ? public void execSQL(String sql) { Log.i("sql execute", sql); sqlite.execSQL(sql); } ? public Cursor getResultSet(String tableName, String condition, Object[] fields) { StringBuffer sb = new StringBuffer(); String allFields = new String(); if (fields == null) { allFields = "*"; } else { for (int i = 0; i < fields.length; i++) { allFields += fields[i].toString() + ","; } allFields = allFields.substring(0, allFields.length() - 1); } sb.append("select ").append(allFields).append(" from ").append( tableName).append(" where ").append(condition); Log.i("sqlquery", sb.toString()); return sqlite.rawQuery(sb.toString(), null); } ? public Cursor getResultSet(String sql) { Log.i("sql query", sql); return sqlite.rawQuery(sql, null); } ? public List<Map<String, String>> getResultSet(String sql, int pageSize) { Log.i("sql query", sql); Cursor cursor = sqlite.rawQuery(sql, null); int count = cursor.getCount(); int columnCount = cursor.getColumnCount(); Log.d("Column Count", "" + columnCount); List<Map<String, String>> list = new ArrayList<Map<String, String>>( count); Map<String, String> entity = new HashMap<String, String>(columnCount); for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { // 找出一共有多少列 for (int i = 0; i < columnCount; i++) { Log.d("Column Found", cursor.getColumnName(i) + " : " + cursor.getString(i)); entity.put(cursor.getColumnName(i), cursor.getString(i)); } list.add(entity); } cursor.close(); entity = null; return list; } ? public void close() { // 关闭我们打开的数据库 throw new RuntimeException("Only for Stub!"); } } |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 | package org.xkit.android.demo; ? import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.util.Log; ? public class MyDatabaseHelper extends SQLiteOpenHelper { ? private static final String TAG = "MyDatabaseHelper"; ? public static final String NAME = "lucane.db"; ? public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } ? public MyDatabaseHelper(Context context, int version) { super(context, NAME, null, version); } ? @Override public void onCreate(SQLiteDatabase db) { bootstrapDB(db); } ? @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.i(TAG, "Upgrading DB from version " + oldVersion + " to " + newVersion); if (oldVersion < 8) { // 如果版本太小,就直接删除,然后创建 // 所以bootstrapDB应该是最新的SQL初始化语句 dropTables(db); onCreate(db); return; } ? if (oldVersion == 8) { upgradeToVersion9(db); oldVersion += 1; } ? if (oldVersion == 9) { upgradeToVersion10(db); oldVersion += 1; } ? // 这是一种逐级更新的方式 Log.v("do upgrade", "我更新了。。。"); } ? private void bootstrapDB(SQLiteDatabase db) { Log.i(TAG, "Bootstrapping database"); db .execSQL("CREATE TABLE person (personid integer primary key autoincrement,name varchar(20),age integer )"); } ? private void dropTables(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS person;"); } ? // PATCH方法开始 ? static void upgradeToVersion10(SQLiteDatabase db) { db.execSQL("CREATE INDEX idx_person_name_gender ON person (" + "name" + ", " + "gender" + ");"); } ? static void upgradeToVersion9(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + "person ADD COLUMN gender INTEGER NOT NULL DEFAULT 1;"); } ? // PATCH方法结束 } |