2010-08-17 72 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; 
}