greendao数据库升级(数据库表的迁移)

本文介绍了一种用于App开发过程中的数据库升级方法,包括通过删除重建的方式处理开发阶段的数据库升级,以及采用迁移辅助类(MigrationHelper)实现数据平滑迁移的上线后升级策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

app开发期间的数据库升级

我们使用DevOpenHelper打开数据库

1
2
 DaoMaster.DevOpenHelper helper = 
new DevOpenHelper(application,"xue_old.db",null);

DevOpenHelper内容

1
2
3
4
5
6
7
8
9
10
11
12
public static class DevOpenHelper extends OpenHelper {
public DevOpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
}
}

其中

1
2
3
4
//删除当前所有表
dropAllTables(db, true);
//新建当前所有表
onCreate(db);

这种方式意味着每次都重新创建,所以数据不会报错

app 上线后的数据库升级,Migration(合并)方式

我们自己新建一个HMROpenHelper,继承自OpenHelper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class HMROpenHelper extends DaoMaster.OpenHelper {

public HMROpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}

/**
* 数据库升级
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//操作数据库的更新
MigrationHelper.migrate(db,NoteDao.class,LessonDao.class,ZhangDao.class);
}

}

数据库的合并如下
MigrationHelper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
public class MigrationHelper {

/**
* 调用升级方法
* @param db
* @param daoClasses 一系列dao.class
*/
public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
//1 新建临时表
generateTempTables(db, daoClasses);
//2 创建新表
createAllTables(db, false, daoClasses);
//3 临时表数据写入新表,删除临时表
restoreData(db, daoClasses);
}


/**
* 生成临时表,存储旧的表数据
* @param db
* @param daoClasses
*/
private static void generateTempTables(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
//方法2
for (int i=0;i<daoClasses.length;i++){
DaoConfig daoConfig = new DaoConfig(db,daoClasses[i]);
String tableName = daoConfig.tablename;
if (!checkTable(db,tableName))
continue;
String tempTableName = daoConfig.tablename.concat("_TEMP");
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("alter table ")
.append(tableName)
.append(" rename to ")
.append(tempTableName)
.append(";");
db.execSQL(insertTableStringBuilder.toString());
}
}

/**
* 检测table是否存在
* @param db
* @param tableName
*/
private static Boolean checkTable(SQLiteDatabase db,String tableName){
StringBuilder query = new StringBuilder();
query.append("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='").append(tableName).append("'");
Cursor c = db.rawQuery(query.toString(), null);
if (c.moveToNext()){
int count = c.getInt(0);
if(count>0){
return true;
}
return false;
}
return false;
}

/**
* 删除所有旧表
* @param db
* @param ifExists
* @param daoClasses
*/
private static void dropAllTables(SQLiteDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "dropTable", ifExists, daoClasses);
}

/**
* 创建新的表结构
* @param db
* @param ifNotExists
* @param daoClasses
*/
private static void createAllTables(SQLiteDatabase db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "createTable", ifNotExists, daoClasses);
}

/**
* 创建根删除都在NoteDao声明了,可以直接拿过来用
* dao class already define the sql exec method, so just invoke it
*/
private static void reflectMethod(SQLiteDatabase db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
if (daoClasses.length < 1) {
return;
}
try {
for (Class cls : daoClasses) {
//根据方法名,找到声明的方法
Method method = cls.getDeclaredMethod(methodName, SQLiteDatabase.class, boolean.class);
method.invoke(null, db, isExists);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

/**
* 临时表的数据写入新表
* @param db
* @param daoClasses
*/
private static void restoreData(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
if (!checkTable(db,tempTableName))
continue;
// get all columns from tempTable, take careful to use the columns list
List<String> columns = getColumns(db, tempTableName);
//新表,临时表都包含的字段
ArrayList<String> properties = new ArrayList<>(columns.size());
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (columns.contains(columnName)) {
properties.add(columnName);
}
}
if (properties.size() > 0) {
final String columnSQL = TextUtils.join(",", properties);

StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(columnSQL);
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(columnSQL);
insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
}
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(dropTableStringBuilder.toString());
}
}

private static List<String> getColumns(SQLiteDatabase db, String tableName) {
List<String> columns = null;
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
if (null != cursor && cursor.getColumnCount() > 0) {
columns = Arrays.asList(cursor.getColumnNames());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
if (null == columns)
columns = new ArrayList<>();
}
return columns;
}
}

其核心思路是
1 把旧表改为临时表
2 建立新表
3 临时表数据写入新表,删除临时表

这样我们就可以直接打开数据库进行更新操作了

1
2
3
HMROpenHelper helper = new HMROpenHelper(application, "xue_old.db", null);
SQLiteDatabase sqlDB = helper.getWritableDatabase();
return sqlDB;

重点

别忘了更新时SCHEMA_VERSION自动加1,不然不会执行更新
位于DaoMaster.java

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值