studio - room database android
SQLiteOpenHelper onUpgrade() Confusion Android (4)
Estoy haciendo mi primera aplicación con una base de datos y estoy teniendo problemas para entender la función de actualización. Mi base de datos tiene una tabla con elementos y una columna favorita para que el usuario pueda preferir un elemento. La mayoría de las implementaciones que veo simplemente dejan caer la tabla y la reconstruyen, pero no quiero hacer esto. Quiero poder agregar más elementos a la mesa.
Cuando la aplicación se actualiza a través del mercado de Android, ¿la base de datos conoce su número de versión? Entonces, ¿podría incrementar el número de versión en el código y luego exportarlo al mercado y cuando el usuario inicie la versión actualizada por primera vez, entonces se llamará a OnUpgrade?
Si este es el caso mi onUpgrade simplemente extraería de un archivo y agregaría los ítems de la base de datos. ¿Es esta una forma estándar de hacer las cosas o hay una mejor manera de manejar esto en Android? Estoy tratando de mantenerme lo más estándar posible.
Gracias
Gracias por aclarar que onUpgrade () no admitirá las sentencias Remove / Drop @Pentium 10
Para aquellos de ustedes que deseen saber el momento exacto en que se llama a onUpgrade (), es durante una llamada a getReadableDatabase () o getWriteableDatabase ().
Para aquellos que no están seguros de cómo se activa, la respuesta es: se activa cuando se actualiza la versión de la base de datos provista al constructor de SqLiteOpenHelper . Aquí hay un ejemplo
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
}
ahora para ... 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);
}
He estado usando la solución propuesta por @ Pentium10 durante mucho tiempo, pero hoy tuve un problema, después de hacer la tabla alter, getColumns de la tabla original aún devuelve las mismas columnas (en la nueva versión del db la tabla sufre una estructura mayor cambios, algunas columnas agregaron otras), realmente no sé por qué select statement no refleja los cambios de la estructura, más antes de volver a crear mi tabla, ¡select statement aún devuelve las columnas! ¡Cuando la tabla no se recrea aún!
Así que me las arreglo para resolver este problema actualizando el método getColumns usando pragma table_info
, así:
/**
* 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;
}
Junto a la excelente respuesta de Pentium10, aquí hay algunos buenos ejemplos del código de la vida:
Ok, antes de que te encuentres con problemas mayores deberías saber que SQLite está limitado en el comando ALTER TABLE, permite add
y rename
solo no remove / drop que se hace con recreación de la tabla.
Siempre debe tener a mano la nueva consulta de creación de tabla, y usarla para actualizar y transferir cualquier información existente. Nota: que los métodos onUpgrade ejecutan uno para su objeto auxiliar sqlite y necesita manejar todas las tablas en él.
Entonces, ¿qué se recomienda en Actualizar?
- comenzarTransacción
- ejecutar una creación de tabla con
if not exists
(estamos haciendo una actualización, por lo que la tabla puede no existir aún, no podrá alterar y soltar) - poner en una lista las columnas existentes
List<String> columns = DBUtils.GetColumns(db, TableName);
- tabla de respaldo (tabla
ALTER table " + TableName + " RENAME TO ''temp_" + TableName
) - crear una nueva tabla (el esquema de creación de tabla más nuevo)
- obtener la intersección con las columnas nuevas, esta vez columnas tomadas de la tabla actualizada (
columns.retainAll(DBUtils.GetColumns(db, TableName));
) - restaurar datos (
String cols = StringUtils.join(columns, ","); db.execSQL(String.format( "INSERT INTO %s (%s) SELECT %s from temp_%s", TableName, cols, cols, TableName));
) - eliminar la tabla de respaldo (
DROP table ''temp_" + TableName
) - setTransactionSuccessful
(Esto no maneja la degradación de la tabla, si cambia el nombre de una columna, no obtiene los datos existentes transferidos ya que los nombres de las columnas no coinciden).
.
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();
}