2010-08-17 3 views
48

Я делаю свое первое приложение с базой данных, и у меня есть небольшая проблема с пониманием функции onUpgrade. В моей базе данных есть таблица с элементами и любимым столбцом, чтобы пользователь мог любить элемент. Большинство реализаций, которые я вижу, просто бросают таблицу и реконструируют ее, но я не хочу этого делать. Я хочу иметь возможность добавлять в таблицу дополнительные элементы.SQLiteOpenHelper onUpgrade() Confusion Android

Когда приложение обновляется на рынке Android, база данных знает его номер версии? Могу ли я увеличить число версий в коде, а затем экспортировать его на рынок и когда пользователь впервые загрузит обновленную версию, тогда будет вызываться onUpgrade?

Если это так, мой onUpgrade просто вытащит из файла и добавит элементы базы данных. Является ли это стандартным способом выполнения действий или есть лучший способ обработки этого в Android. Я стараюсь оставаться как можно более стандартным.

Благодаря

+65

Вы знаете, Майк, это был год. Вы действительно должны принять один из ответов – Noah

+5

Последнее посещение: 7 сентября '10 в 17:35 –

+11

лет идут, но Майк не называет 'onUpgrade()' –

ответ

108

Ok, прежде чем вы столкнетесь с большими проблемами, вы должны знать, что SQLite ограничена по команде ALTER TABLE, она позволяет add и rename только не удалить/падение, которое сделано с отдыхом стола.

У вас всегда должен быть новый запрос создания таблицы и использовать его для обновления и переноса любых существующих данных. Обратите внимание: методы 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

Hi Pentium, отличный ответ, мне было интересно, можете ли вы уточнить, как я «получить пересечение с новыми столбцами, на этот раз столбцы, взятые из обновленной таблицы», хотя? –

+1

он есть в коде, это метод keepAll в списке, поэтому 'oldColumns.retainAll (newColumns)' в это время 'DBUtils.GetColumns (db, TableName)' будет возвращать новые столбцы, так как новые таблицы только что были созданы, и переменная 'columns' содержит ссылку на столбцы перед переименованием таблицы, поэтому старые столбцы. – Pentium10

+0

Спасибо Pentuim, я запутался в дополнительных скобках, которые у вас были вокруг кода, я думал, что вы используете добавочную переменную или что-то вне скобок, отличный ответ и спасибо снова. –

39

Рядом с отличным ответом Pentium10, вот некоторые хорошие примеры из живого кода:

+1

+1, чрезвычайно полезно – orip

+0

К сожалению, Google Code Search закрылся, поэтому эти примеры не могут быть просмотрены. –

+0

Печально. Я немного его отредактировал. Вы можете искать, нет? – pjv

6

Спасибо за разъяснение, что onUpgrade() не будет поддерживать заявления Remove/Drop @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

+0

Извините. Старые привычки умирают, я думаю ... – lazyList

+2

Что такое прецедент для обновления номера DB_VERSION? –

1

Я использовал решение, предложенное @ Pentium10 в течение длительного времени, но сегодня у меня была проблема, после выполнения alter table, getColumns из исходной таблицы по-прежнему возвращает те же столбцы (в новой версии db таблица страдает изменениями структуры мэра, некоторые столбцы добавили некоторые другие), действительно я не знаю, почему select statement не отражает изменения структуры , больше, прежде чем снова создать таблицу, оператор 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; 
} 
Смежные вопросы