Android 数据库升级保留原数据不丢

相信不少人在做项目时会数据库升级,可能涉及到数据结构的变化,下面写了个工具类,可以帮助在数据结构变化时,保留原数据不丢。

/**
 * Created by zeng on 2017/1/13.
 * 为了适用于如greenDao这样的框架,使用反射调用相应的方法
 */
public class DbUtils {
	
	public static class Column {
		public static final String INTEGER = "INTEGER";
		public static final String TEXT = "TEXT";
		public static final String REAL = "REAL";
		public static final String DATE = "DATE";
		public String name;
		public String type;
		public Object defualtValue;

		public Column() {
		}

		public Column(String name, String type, Object defualtValue) {
			this.name = name;
			this.type = type;
			this.defualtValue = defualtValue;
		}
	}
	
	private static void execSQL(Object db, String sql) {
		try {
			db.getClass().getMethod("execSQL", String.class).invoke(db, sql);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void execSQL(Object db, String sql, Object[] bindArgs) {
		try {
			db.getClass().getMethod("execSQL", String.class, Object[].class).invoke(db, sql, bindArgs);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void invokeVoidNoPrama(Object db, String methodName) {
		try {
			db.getClass().getMethod(methodName).invoke(db);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void beginTransaction(Object db) {
		invokeVoidNoPrama(db, "beginTransaction");
	}
	
	private static void endTransaction(Object db) {
		invokeVoidNoPrama(db, "endTransaction");
	}
	
	private static void setTransactionSuccessful(Object db) {
		invokeVoidNoPrama(db, "setTransactionSuccessful");
	}
	
	/**
	 * 重命名表
	 */
	public static void renameTable(Object db, String oldName, String newName) {
		try {
			execSQL(db, "ALTER TABLE " + oldName + " RENAME TO " + newName);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 删除表
	 */
	public static void deleteTable(Object db, String tableName) {
		try {
			execSQL(db, "DROP TABLE " + tableName);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取数据表的所有字段名
	 */
	public static Column[] getColumns(Object db, String tableName) {
		Column[] columns = null;
		Cursor cursor = null;
		try {
			String sql = "PRAGMA table_info(" + tableName + ")";
			Method method = db.getClass().getMethod("rawQuery", String.class, String[].class);
			cursor = (Cursor) method.invoke(db, sql, null);
			int nameIndex = cursor.getColumnIndex("name");
			if (nameIndex == -1) {
				return null;
			}
			int index = 0;
			columns = new Column[cursor.getCount()];
			for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
				columns[index] = new Column();
				columns[index].name = cursor.getString(nameIndex);
				columns[index].type = cursor.getString(cursor.getColumnIndex("type"));
				int defaultValueIndex = cursor.getColumnIndex("dflt_value");
				switch(cursor.getType(defaultValueIndex)) {
				    case Cursor.FIELD_TYPE_BLOB:
						columns[index].defualtValue = cursor.getBlob(defaultValueIndex);
						break;
				    case Cursor.FIELD_TYPE_FLOAT:
						columns[index].defualtValue = cursor.getDouble(defaultValueIndex);
						break;
					case Cursor.FIELD_TYPE_INTEGER:
						columns[index].defualtValue = cursor.getLong(defaultValueIndex);
						break;
					case Cursor.FIELD_TYPE_STRING:
						columns[index].defualtValue = cursor.getString(defaultValueIndex);
						break;
				}
				index++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		}
		return columns;
	}

	/**
	 * 删除列
	 * @param columnNames 要删除的列名
	 */
	public static void deleteColumns(Object db, String tableName, String... columnNames) {
		try {
			beginTransaction(db);
			Column[] columns = getColumns(db, tableName);
			if (columns != null) {
				//重命名原表
				String tmpTableName = "t" + UUID.randomUUID().toString().replaceAll("-", "_");
				execSQL(db, "ALTER TABLE " + tableName + " RENAME TO " + tmpTableName);
				//新建表sql语句
				String newTableSql = "CREATE TABLE " + tableName + "(";
				List<String> list = Arrays.asList(columnNames);
				//复制数据sql语句
				String copySql = "INSERT INTO " + tableName + " SELECT ";
				for (Column column : columns) {
					if (!list.contains(column.name)) { 
						newTableSql += column.name + " " + column.type + ",";
						copySql += column.name + ",";
					}
				}
				newTableSql = newTableSql.substring(0, newTableSql.length() - 1) + ")";
				execSQL(db, newTableSql);
				//复制数据到新表
				copySql = copySql.substring(0, copySql.length() - 1) + " FROM " + tmpTableName;
				execSQL(db, copySql);
				//删除临时表
				execSQL(db, "DROP TABLE " + tmpTableName);
			}
			setTransactionSuccessful(db);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			endTransaction(db);			
		}		
	}

	/**
	 * 添加列
	 * @param columns 需要添加的列的信息
	 */
	public static void addColumns(Object db, String tableName, Column... columns) {
		try {
			beginTransaction(db);
			for (Column column : columns) {
				String sql = "ALTER TABLE " + tableName + " ADD " + column.name + " " + column.type;
				Object[] bindArgs = null;
				if (column.defualtValue != null) {
					if (column.defualtValue instanceof String) {
					    sql += " DEFAULT '" + column.defualtValue + "'";
					} else if (column.defualtValue instanceof byte[]) {
						//如果是字节数组,先插入列,再设置默认数据
					    execSQL(db, sql);
						sql = "UPDATE " + tableName + " SET " + column.name + "=?";
						bindArgs = new Object[]{column.defualtValue};
					} else {
					    sql += " DEFAULT " + column.defualtValue;
					}
				}
				if (bindArgs == null) {
				    execSQL(db, sql);
				} else {
					execSQL(db, sql, bindArgs);
				}
			}
			setTransactionSuccessful(db);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			endTransaction(db);
		}
	}

	/**
	 * 更新列名
	 * @param map key为旧列名,value为新列名
	 */
	public static void updateColumnNames(Object db, String tableName, Map<String, String> map) {
		try {
			beginTransaction(db);
			Column[] columns = getColumns(db, tableName);
			if (columns != null) {
				//重命名原表
				String tmpTableName = "t" + UUID.randomUUID().toString().replaceAll("-", "_");
				execSQL(db, "ALTER TABLE " + tableName + " RENAME TO " + tmpTableName);
				//新建表sql语句
				String newTableSql = "CREATE TABLE " + tableName + "(";
				//复制数据sql语句
				String copySql = "INSERT INTO " + tableName + " SELECT ";
				Set<String> keySet = map.keySet();
				for (Column column : columns) {
					if (keySet.contains(column.name)) {
					    newTableSql += map.get(column.name) + " " + column.type + ",";
					} else {
					    newTableSql += column.name + " " + column.type + ",";
					}
					copySql += column.name + ",";
				}
				newTableSql = newTableSql.substring(0, newTableSql.length() - 1) + ")";
				execSQL(db, newTableSql);
				//复制数据到新表
				copySql = copySql.substring(0, copySql.length() - 1) + " FROM " + tmpTableName;
				execSQL(db, copySql);
				//删除临时表
				execSQL(db, "DROP TABLE " + tmpTableName);
			}
			setTransactionSuccessful(db);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			endTransaction(db);
		}
	}
}

源码下载

猜你喜欢

转载自blog.csdn.net/fszeng2011/article/details/54561367