Android 数据库升级解决方案 .

请考虑如下情况:

在数据库升级时,不同版本的数据库,他们定义的表结构完全可能是不一样的,比如V1.0的表A有10个column,而在V1.1的表A有12个colum,在升级时,表A增加了两列,此时我们应该怎么做呢。


总体思路

1,将表A重命名,改了A_temp。

2,创建新表A。

3,将表A_temp的数据插入到表A。

下面代码列出了更新表的实现,upgradeTables,给定表名,更新的列名,就可以实现数据库表的更新。

  1. /** 
  2.  * Upgrade tables. In this method, the sequence is: 
  3.  * <b> 
  4.  * <p>[1] Rename the specified table as a temporary table. 
  5.  * <p>[2] Create a new table which name is the specified name. 
  6.  * <p>[3] Insert data into the new created table, data from the temporary table. 
  7.  * <p>[4] Drop the temporary table. 
  8.  * </b> 
  9.  * 
  10.  * @param db The database. 
  11.  * @param tableName The table name. 
  12.  * @param columns The columns range, format is "ColA, ColB, ColC, ... ColN"; 
  13.  */  
  14. protected void upgradeTables(SQLiteDatabase db, String tableName, String columns)  
  15. {  
  16.     try  
  17.     {  
  18.         db.beginTransaction();  
  19.   
  20.         // 1, Rename table.   
  21.         String tempTableName = tableName + "_temp";  
  22.         String sql = "ALTER TABLE " + tableName +" RENAME TO " + tempTableName;  
  23.         execSQL(db, sql, null);  
  24.   
  25.         // 2, Create table.   
  26.         onCreateTable(db);  
  27.   
  28.         // 3, Load data   
  29.         sql =   "INSERT INTO " + tableName +  
  30.                 " (" + columns + ") " +  
  31.                 " SELECT " + columns + " FROM " + tempTableName;  
  32.   
  33.         execSQL(db, sql, null);  
  34.   
  35.         // 4, Drop the temporary table.   
  36.         execSQL(db, "DROP TABLE IF EXISTS " + tempTableName, null);  
  37.   
  38.         db.setTransactionSuccessful();  
  39.     }  
  40.     catch (SQLException e)  
  41.     {  
  42.         e.printStackTrace();  
  43.     }  
  44.     catch (Exception e)  
  45.     {  
  46.         e.printStackTrace();  
  47.     }  
  48.     finally  
  49.     {  
  50.         db.endTransaction();  
  51.     }  
  52. }  
/**
 * Upgrade tables. In this method, the sequence is:
 * <b>
 * <p>[1] Rename the specified table as a temporary table.
 * <p>[2] Create a new table which name is the specified name.
 * <p>[3] Insert data into the new created table, data from the temporary table.
 * <p>[4] Drop the temporary table.
 * </b>
 *
 * @param db The database.
 * @param tableName The table name.
 * @param columns The columns range, format is "ColA, ColB, ColC, ... ColN";
 */
protected void upgradeTables(SQLiteDatabase db, String tableName, String columns)
{
    try
    {
        db.beginTransaction();

        // 1, Rename table.
        String tempTableName = tableName + "_temp";
        String sql = "ALTER TABLE " + tableName +" RENAME TO " + tempTableName;
        execSQL(db, sql, null);

        // 2, Create table.
        onCreateTable(db);

        // 3, Load data
        sql =   "INSERT INTO " + tableName +
                " (" + columns + ") " +
                " SELECT " + columns + " FROM " + tempTableName;

        execSQL(db, sql, null);

        // 4, Drop the temporary table.
        execSQL(db, "DROP TABLE IF EXISTS " + tempTableName, null);

        db.setTransactionSuccessful();
    }
    catch (SQLException e)
    {
        e.printStackTrace();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        db.endTransaction();
    }
}

得到数据库表的列名

我们可以通过SQL表得到表的列名。 这里需要注意的一点,int columnIndex = c.getColumnIndex("name"); 这里根据name去取得index。 

  1. protected String[] getColumnNames(SQLiteDatabase db, String tableName)  
  2. {  
  3.     String[] columnNames = null;  
  4.     Cursor c = null;  
  5.   
  6.     try  
  7.     {  
  8.         c = db.rawQuery("PRAGMA table_info(" + tableName + ")"null);  
  9.         if (null != c)  
  10.         {  
  11.             int columnIndex = c.getColumnIndex("name");  
  12.             if (-1 == columnIndex)  
  13.             {  
  14.                 return null;  
  15.             }  
  16.   
  17.             int index = 0;  
  18.             columnNames = new String[c.getCount()];  
  19.             for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext())  
  20.             {  
  21.                 columnNames[index] = c.getString(columnIndex);  
  22.                 index++;  
  23.             }  
  24.         }  
  25.     }  
  26.     catch (Exception e)  
  27.     {  
  28.         e.printStackTrace();  
  29.     }  
  30.     finally  
  31.     {  
  32.         closeCursor(c);  
  33.     }  
  34.   
  35.     return columnNames;  
  36. }  
protected String[] getColumnNames(SQLiteDatabase db, String tableName)
{
    String[] columnNames = null;
    Cursor c = null;

    try
    {
        c = db.rawQuery("PRAGMA table_info(" + tableName + ")", null);
        if (null != c)
        {
            int columnIndex = c.getColumnIndex("name");
            if (-1 == columnIndex)
            {
                return null;
            }

            int index = 0;
            columnNames = new String[c.getCount()];
            for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext())
            {
                columnNames[index] = c.getString(columnIndex);
                index++;
            }
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        closeCursor(c);
    }

    return columnNames;
}


upgradeTables方法应该是在onUpgrade方法中去调用。


数据库升级的意义

在应用程序开发的过程中,数据库的升级是一个很重要的组成部分(如果用到了数据库),因为程序可能会有V1.0,V2.0,当用户安装新版本的程序后,必须要保证用户数据不能丢失,对于数据库设计,如果发生变更(如多添加一张表,表的字段增加或减少等),那么我们必须想好数据库的更新策略。

1,定义数据库版本

数据库的版本是一个整型值,在创建SQLiteOpenHelper时,会传入该数据库的版本,如果传入的数据库版本号比数据库文件中存储的版本号大的话,那么SQLiteOpenHelper#onUpgrade()方法就会被调用,我们的升级应该在该方法中完成。

2,如何写升级逻辑

假如我们开发的程序已经发布了两个版本:V1.0,V1.2,我们正在开发V1.3。每一版的数据库版本号分别是18,19,20。

对于这种情况,我们应该如何实现升级?

用户的选择有:                   

1) V1.0 -> V1.3  DB 18 -> 20                  

2) V1.1 -> V1.3  DB 19 -> 20      

3,注意

数据库的每一个版本所代表的数据库必须是定义好的,比如说V18的数据库,它可能只有两张表TableA和TableB,如果V19要添加一张表TableC,如果V20要修改TableC,那么每一个版本所对应的数据库结构如下:

V18  --->  TableA, TableB

V19  --->  TableA, TableB, TableC

V20  --->  TableA, TableB, TableC (变更)

onUpgrade()方法的实现如下:

  1.        // Pattern for upgrade blocks:   
  2. //   
  3. //    if (upgradeVersion == [the DATABASE_VERSION you set] - 1){   
  4. //        .. your upgrade logic..   
  5. //        upgradeVersion = [the DATABASE_VERSION you set]   
  6. //    }   
  7.   
  8.   
  9. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)  
  10. {  
  11.     int upgradeVersion  = oldVersion;  
  12.   
  13.     if (18 == upgradeVersion) {  
  14.         // Create table C   
  15.         String sql = "CREATE TABLE ...";  
  16.         db.execSQL(sql);  
  17.         upgradeVersion = 19;  
  18.     }  
  19.   
  20.     if (20 == upgradeVersion) {  
  21.         // Modify table C   
  22.         upgradeVersion = 20;  
  23.     }  
  24.   
  25.     if (upgradeVersion != newVersion) {  
  26.         // Drop tables   
  27.         db.execSQL("DROP TABLE IF EXISTS " + tableName);  
  28.         // Create tables   
  29.         onCreate(db);  
  30.     }  
  31. }  
        // Pattern for upgrade blocks:
	//
	//    if (upgradeVersion == [the DATABASE_VERSION you set] - 1){
	//        .. your upgrade logic..
	//        upgradeVersion = [the DATABASE_VERSION you set]
	//    }


	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
	{
		int upgradeVersion  = oldVersion;

		if (18 == upgradeVersion) {
			// Create table C
			String sql = "CREATE TABLE ...";
			db.execSQL(sql);
			upgradeVersion = 19;
		}

		if (20 == upgradeVersion) {
			// Modify table C
			upgradeVersion = 20;
		}

		if (upgradeVersion != newVersion) {
			// Drop tables
			db.execSQL("DROP TABLE IF EXISTS " + tableName);
			// Create tables
			onCreate(db);
		}
	}

从上面的代码可以看到,我们在onUpgrade()方法中,处理了数据库版本从18 -> 20的升级过程,这样做的话,不论用户从18 -> 20,还是从19 -> 20,最终程序的数据库都能升级到V20所对应的数据库结构。  

4,如何保证数据不丢失

这是很重要的一部分,假设要更新TableC表,我们建议的做法是:       

1) 将TableC重命名为TableC_temp

       SQL语句可以这样写:ALERT TABLE TableC RENAME TO TableC_temp;

2) 创建新的TableC表

3) 将数据从TableC_temp中插入到TableC表中

       SQL语句可以这样写:INSERT INTO TableC (Col1, Col2, Col3) SELECT (Col1, Col2, Col3) FROM TableC_temp;                

                   
经过这三步,TableC就完成了更新,同时,也保留了原来表中的数据。  

注意:

在onUpgrade()方法中,删除表时,注意使用事务处理,使得修改能立即反应到数据库文件中。    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值