SQLiteOpenHelper onUpgrade () Путаница Android

48

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

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

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

Спасибо

  • 65
    Вы знаете, Майк, это был год. Вы действительно должны принять один из ответов
  • 5
    Последнее посещение: 7 сентября 2010 г. в 17:35
Показать ещё 5 комментариев
Теги:
database

4 ответа

113

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

Вы всегда должны иметь новый запрос создания таблицы и использовать его для обновления и переноса любых существующих данных. Обратите внимание: методы onUpgrade запускают один для вашего вспомогательного объекта sqlite, и вам нужно обрабатывать все его таблицы.

Итак, что рекомендуется наUpgrade:

  • 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
    Привет, Pentium, отличный ответ, мне было интересно, не могли бы вы пояснить, как я "получаю пересечение с новыми столбцами, хотя на этот раз столбцы взяты из обновленной таблицы"?
  • 1
    он есть в коде, это метод retainAll в списке, поэтому oldColumns.retainAll(newColumns) в это время DBUtils.GetColumns(db, TableName) будет возвращать новые столбцы, так как новые таблицы только что были созданы, а переменная columns содержит ссылка на столбцы до переименования таблицы, поэтому старые столбцы.
Показать ещё 6 комментариев
38

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

  • 1
    +1, очень полезно
  • 0
    К сожалению, поиск кода Google сейчас закрыт, поэтому эти примеры нельзя просмотреть.
Показать ещё 2 комментария
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
    Пожалуйста, не подписывайте свои сообщения. Поместите свой сайт в свой профиль.
  • 0
    Извини за это. Старые привычки умирают, я думаю ...
Показать ещё 1 комментарий
1

Я использую решение, предлагаемое @Pentium10 в течение длительного времени, но сегодня у меня возникла проблема, после выполнения alter table, getColumns из исходной таблицы по-прежнему возвращает те же столбцы (в новой версии db таблицы пострадают изменения структуры мэра, некоторые столбцы добавили некоторые другие), действительно я не знаю, почему оператор select не отражает изменения структуры, более того, прежде чем снова создать таблицу, select statement по-прежнему возвращает столбцы! Когда таблица еще не была воссоздана!

Итак, я решаю решение этой проблемы, обновляя метод getColumns с помощью pragma table_info, например:

    /**
 * 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;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню