都知道在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方法结束
}
|