继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android的数据库的写法(如何升级数据库)

nickcau
关注TA
已关注
手记 113
粉丝 6507
获赞 303

对数据库的升级操作:

中心思想是:

对比新旧的database结构,如果旧表中有table新的database里没有,则删除旧表里的

如果新database中有一个table更新,则需要更新这个表的结构,然后把旧表中数据拷贝过来

(把旧表rename一个名字,把命名后的表中的数据拷贝到新表中)


原本以为很简单的问题,直接在原来创建数据库的语句中加上需要添加的列new_column,但是运行时发现,应用crash。原因是,原有数据库文件已经存在的情况下并不会重新创建数据库,也就是说此时数据库中并没有new_column列,这个时候,运行query()来查询数据库如果包括new_column列,就会导致应用crash。 其实,SQLite提供了数据库升级的方法 onUpgrade(),只需要在创建SQLiteOpenHelper对象时,传人比原版本号更大的版本号,便会触发该方法,在这个方法中可以实现迁移原有数据及创建新数据库Table。 在某些情况下,数据库版本会出现降低的情况:如用户安装了版本更低的应用,但是应用数据没有改变。这种情况下,如果设计数据库时没有override onDowngrade()方法,会导致应用崩溃。所以,在设计数据库时,最好能override onDowngrade()方法,防止这种情况下应用崩溃的发生

写一个数据库升级帮助类DbUtils:

final class DbUtils {
    
    private static final String TAG = "DbUtils";
    private static final boolean DEBUG = false;
    
    private static final String SQLITE_STMT_LIST_TABLES =
        "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE 'android%'";
    private static final String SQLITE_TABLE_NAME_COLUMN = "name";
    private static final String SQLITE_STMT_TEMPLATE_LIST_COLUMNS = "SELECT * FROM %s LIMIT 1";
    private static final String SQLITE_STMT_TEMPLATE_DROP_TABLE = "DROP TABLE IF EXISTS %s";
    private static final String SQLITE_STMT_TEMPLATE_RENAME_TABLE = "ALTER TABLE %s RENAME TO %s";
    private static final String SQLITE_STMT_TEMPLATE_COPY_COLUMNS = "INSERT INTO %s (%s) SELECT %s FROM %s";

    
    /**
     * @param db
     * @return Collection object containing table names in the database 
     */
    static Collection<String> listTables(SQLiteDatabase db) {

        Cursor cursor = db.rawQuery(SQLITE_STMT_LIST_TABLES, null);
        if (cursor == null || !cursor.moveToFirst()) {

            if (cursor != null) {
                cursor.close();
            }
            return null;
        }
        
        int table_name_column = cursor.getColumnIndex(SQLITE_TABLE_NAME_COLUMN);
        HashSet<String> tables = new HashSet<String>(cursor.getCount());
        do {
            tables.add(cursor.getString(table_name_column));
        } while (cursor.moveToNext());
        cursor.close();

        
        return tables;
    }

    
    /**
     * @param db
     * @param table
     * @return List of column names in the DB table
     */
    public static List<String> listColumns(SQLiteDatabase db, String table) {
        
        Cursor cursor = db.rawQuery(String.format(SQLITE_STMT_TEMPLATE_LIST_COLUMNS, table), null);
        if (cursor == null) {
            return null;
        }
        
        List<String> columns = Arrays.asList(cursor.getColumnNames());
        cursor.close();

        return columns;
    }
    
    
    /**
     * @param db
     * @param table
     */
    static void dropTable(SQLiteDatabase db, String table) {

        db.execSQL(String.format(SQLITE_STMT_TEMPLATE_DROP_TABLE, table));
    }
    

    static void renameTable(SQLiteDatabase db, String oldName, String newName) {

        db.execSQL(String.format(SQLITE_STMT_TEMPLATE_RENAME_TABLE, oldName, newName));
    }
    
    
    static void joinColumns(SQLiteDatabase db, String oldTable, String newTable) {
        
        //Delete all records in the new table before copying from the old table
        db.delete(newTable, null, null);
        
        //Find columns which exist in both tables
        ArrayList<String> old_columns = new ArrayList<String>(listColumns(db, oldTable));
        List<String> new_columns = listColumns(db, newTable);
        old_columns.retainAll(new_columns);

        String common_columns = TextUtils.join(",", old_columns);
        
        //Copy records from old table to new table
        System.out.println("====DBHelp joinColumns sql ="+String.format(SQLITE_STMT_TEMPLATE_COPY_COLUMNS, newTable, common_columns, common_columns, oldTable));
        db.execSQL(String.format(SQLITE_STMT_TEMPLATE_COPY_COLUMNS, newTable, common_columns, common_columns, oldTable));
    }

}

这些函数都是执行sql方法,插入,修改还有查询

然后在DbHelper中重写onUpgrade方法:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    //Get table names in the old DB
    Collection<String> old_tables = DbUtils.listTables(db);
    if (old_tables == null || old_tables.size() == 0) {
        onCreate(db);
        return;
    }
    
    //Get table names in the new DB
    Set<String> new_tables = DataBaseClass.sRCMDbTables.keySet();
    
    try {
        db.beginTransaction();
        //Remove old tables which are not in the new DB scheme
        HashSet<String> obsolete_tables = new HashSet<String>();
        for (String table : old_tables) {
            if (!new_tables.contains(table)) {
               System.out.println("====DBHelp onUpgrade droptable table="+table);
                DbUtils.dropTable(db, table);
                obsolete_tables.add(table);
            }
        }
        old_tables.removeAll(obsolete_tables);

        //Create and upgrade new tables 
        DbBaseTable table_descriptor; 
        for (String table : new_tables) {
            table_descriptor = DataBaseClass.sRCMDbTables.get(table);
            
            //Check if the new table exists in the old DB
            if (old_tables.contains(table)) {
                String temp_name = getTempTableName(table, old_tables, new_tables);
                System.out.println("====DBHelp onUpgrade temp_name ="+temp_name);
                table_descriptor.onUpgrade(db, oldVersion, newVersion, temp_name);
            } else {
                table_descriptor.onCreate(db);
            }
        }
        db.setTransactionSuccessful();
    } catch (Throwable e) {
        
        throw new RuntimeException("DB upgrade failed: " + e.getMessage());
    } finally {
        db.endTransaction();
    }
}


private String getTempTableName(String tableName, Collection<String> oldTableNames, Set<String> newTableNames) {
    String temp_name_base = tableName + TEMP_SUFFIX;
     
    if (!oldTableNames.contains(temp_name_base) && !newTableNames.contains(temp_name_base)) {
        return temp_name_base;
    }

    Random random = new Random();
    String temp_name;
    for (;;) {
        temp_name = temp_name_base + random.nextInt();
        if (!oldTableNames.contains(temp_name) && !newTableNames.contains(temp_name)) {
            return temp_name;
        }
    }
}

代码在https://github.com/nickgao1986/StepSport

打开App,阅读手记
4人推荐
发表评论
随时随地看视频慕课网APP