2010-08-17 69 views
48

我正在做我的第一个应用程序与数据库,我有点麻烦了解onUpgrade函数。我的数据库有一个包含项目和最喜爱的列的表格,以便用户可以收藏项目。我看到的大多数实现只是删除表并重构它,但我不想这样做。我希望能够添加更多的项目到表格中。SQLiteOpenHelper onUpgrade()混淆Android

当应用程序通过android marketplace升级时,数据库是否知道它的版本号?因此,我可以在代码中增加版本号,然后将其导出到市场,并且当用户首次启动升级版本时,onUpgrade将被调用?

如果是这种情况,我的onUpgrade会简单地从文件中拉出并添加数据库项。这是一种标准的做事方式,还是在Android中处理这种情况的更好方法。我尽可能保持标准。

感谢

+65

你知道迈克,它已经过了一年。你真的应该接受其中一个答案 – Noah 2011-10-18 13:42:32

+5

最后看到:09年9月7日在17:35 – 2012-12-05 06:34:41

+11

年,但迈克不要叫'onUpgrade()' – 2013-08-01 06:52:15

回答

108

好吧,你遇到了更大的问题之前,你应该知道,SQLite是在ALTER TABLE命令的限制,它允许addrename唯一没有remove /下降这与表的娱乐做。

您应始终拥有新的表创建查询,并将其用于升级并传输任何现有数据。注意:onUpgrade方法为你的sqlite帮助对象运行一个,你需要处理它中的所有表。

所以建议采取什么onUpgrade:

  • 的BeginTransaction
  • 运行与if not exists表生成(我们正在做的升级,因此该表可能还不存在,它将无法修改和删除)
  • 放在一个列表中的现有列List<String> columns = DBUtils.GetColumns(db, TableName);
  • 备份表(ALTER table " + TableName + " RENAME TO 'temp_" + TableName
  • 创建新表(最新表创建模式)
  • 获得与新列的交叉点,从升级后的表采取这一时间列(columns.retainAll(DBUtils.GetColumns(db, TableName));
  • 恢复数据(String cols = StringUtils.join(columns, ","); db.execSQL(String.format( "INSERT INTO %s (%s) SELECT %s from temp_%s", TableName, cols, cols, TableName));
  • 删除备份表(DROP table 'temp_" + TableName
  • setTransactionSuccessful

(这不处理表降级,如果您重命名列,你没有得到转移作为列名不匹配现有的数据)。

public static List<String> GetColumns(SQLiteDatabase db, String tableName) { 
    List<String> ar = null; 
    Cursor c = null; 
    try { 
     c = db.rawQuery("select * from " + tableName + " limit 1", null); 
     if (c != null) { 
      ar = new ArrayList<String>(Arrays.asList(c.getColumnNames())); 
     } 
    } catch (Exception e) { 
     Log.v(tableName, e.getMessage(), e); 
     e.printStackTrace(); 
    } finally { 
     if (c != null) 
      c.close(); 
    } 
    return ar; 
} 

public static String join(List<String> list, String delim) { 
    StringBuilder buf = new StringBuilder(); 
    int num = list.size(); 
    for (int i = 0; i < num; i++) { 
     if (i != 0) 
      buf.append(delim); 
     buf.append((String) list.get(i)); 
    } 
    return buf.toString(); 
} 
+0

嗨奔腾,优秀的答案,我想知道你能澄清我“获取与新列的交集,这次从升级后的表中获取列”,但? – 2010-09-15 09:13:54

+1

它在代码中就是列表中的retainAll方法,因此''oldColumns.retainAll(newColumns)''DBUtils.GetColumns(db,TableName)'将返回新列,因为新表刚创建完毕,并且'columns'变量在重命名表之前保留对列的引用,所以旧列。 – Pentium10 2010-09-15 10:38:08

+0

感谢Pentuim,我对你在代码中使用的额外括号感到困惑,我以为你使用了一个额外的变量或括号外的东西,非常好的答案,并再次感谢。 – 2010-09-15 13:23:44

39

下一步Pentium10的出色答卷,这里是从活代码一些很好的例子:

+1

+1,非常有用 – orip 2011-11-02 15:18:42

+0

很遗憾,Google代码搜索已经关闭,因此无法查看这些示例。 – 2012-03-13 11:31:42

+0

很伤心。我已经编辑了一下。你可以搜索,不是吗? – pjv 2012-03-20 19:25:52

6

感谢您澄清onUpgrade()将不支持删除/删除语句@Pentium 10

对于那些你想知道的确切时刻谁当onUpgrade()被调用,它是一个过程调用getReadableDatabase()或getWriteableDatabase()。

对于那些不清楚如何确保它被触发的答案是:当提供给SqLiteOpenHelper的构造函数的数据库版本更新为时,会触发它。下面是一个例子

public class dbSchemaHelper extends SQLiteOpenHelper { 

private String sql; 
private final String D_TAG = "FundExpense"; 
//update this to get onUpgrade() method of sqliteopenhelper class called 
static final int DB_VERSION = 2; 
static final String DB_NAME = "fundExpenseManager"; 

public dbSchemaHelper(Context context) { 
    super(context, DB_NAME, null, DB_VERSION); 
    // TODO Auto-generated constructor stub 
} 

现在... onUpgrade()

@Override 
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) { 
    sql = "ALTER TABLE " + fundExpenseSchema.Expense.TABLE_NAME + " ADD COLUMN " + fundExpenseSchema.Expense.FUNDID + " INTEGER"; 
    arg0.execSQL(sql); 
} 
+0

请不要在你的帖子上签名。把你的网站放到你的个人资料中 – Amy 2011-12-23 05:02:45

+0

对不起。老习惯很难死,我猜... – lazyList 2011-12-24 00:01:38

+2

什么是升级DB_VERSION号码的用例? – 2013-07-10 16:46:18

1

我已经使用了很长一段时间提出@ Pentium10解决了,但今天我有一个问题,在做后alter table,getColumns从原始表仍然返回相同的列(在新版本的数据库表受到市长结构变化,一些列添加了一些其他),我真的不知道为什么选择语句不反映结构变化,再次创建我的表之前,select语句仍然返回列!当表格不被重新创建时!

所以我设法解决这个问题,更新使用 getColumns方法,像这样:

/** 
* Get a list of column base_dictionary for the selected table 
* 
* @param db 
* Database that contains the table 
* @param tableName 
* Table name to be used 
* @return A List of column name 
*/ 
public static List<String> getColumns(SQLiteDatabase db, String tableName) { 
    List<String> ar = null; 
    Cursor c = null; 
    try { 
     c = db.rawQuery("pragma table_info(" + tableName + ")", null); 

     ar = new ArrayList<String>(); 
     if (c != null && c.moveToFirst()) { 
      do { 
       ar.add(c.getString(c.getColumnIndexOrThrow("name"))); 
      } while (c.moveToNext()); 
      c.close(); 
     } 

    } catch (Exception e) { 
     Log.v(tableName, e.getMessage(), e); 
     e.printStackTrace(); 
    } finally { 
     if (c != null) c.close(); 
    } 
    return ar; 
}