Prácticas recomendadas para trabajar con múltiples tablas

Utilizo una base de datos con varias tablas en mi aplicación. Tengo un analizador de XML que necesita escribir datos a dos tablas mientras que analiza. He creado dos adaptadores de base de datos para ambas tablas, pero ahora tengo un problema. Cuando estoy trabajando con una mesa, es fácil:

FirstDBAdapter firstTable = new FirstDBAdapter(mycontext); firstTable.open(); // open and close it every time I need to insert something // may be hundreds of times while parsing // it opens not a table but whole DB firstTable.insertItem(Item); firstTable.close(); 

Puesto que es un analizador de SAX, en mi opinión (tal vez estoy equivocado), esto será aún mejor:

 FirstDBAdapter firstTable = new FirstDBAdapter(mycontext); @Override public void startDocument() throws SAXException { firstTable.open(); // open and close only once } ... firstTable.insertItem(Item); ... @Override public void endDocument() throws SAXException { firstTable.close(); } 

Pero, ¿cómo lo hago si necesito insertar datos en la segunda tabla? Por ejemplo, si tengo el segundo adaptador, que creo que será una mala idea:

 FirstDBAdapter firstTable = new FirstDBAdapter(mycontext); SecondDBAdapter secondTable = new SecondDBAdapter(mycontext); @Override public void startDocument() throws SAXException { firstTable.open(); secondTable.open(); } 

¿Alguna idea sobre cómo lograrlo?

Mi adaptador de base de datos. Una instancia siempre se almacena en MyApplication que hereda de Application. Basta pensar en una segunda tabla en la que definí la primera … actualmente esto es sólo una versión corta, en realidad este adaptador maneja 7 tablas de la base de datos.

 public class MyDbAdapter { private static final String LOG_TAG = MyDbAdapter.class.getSimpleName(); private SQLiteDatabase mDb; private static MyDatabaseManager mDbManager; public MyDbAdapter() { mDbManager = new MyDatabaseManager(MyApplication.getApplication()); mDb = mDbManager.getWritableDatabase(); } public static final class GameColumns implements BaseColumns { public static final String TABLE = "game"; public static final String IMEI = "imei"; public static final String LAST_UPDATE = "lastupdate"; public static final String NICKNAME = "nickname"; } public String getImei() { checkDbState(); String retValue = ""; Cursor c = mDb.rawQuery("SELECT imei FROM " + GameColumns.TABLE, null); if (c.moveToFirst()) { retValue = c.getString(c.getColumnIndex(GameColumns.IMEI)); } c.close(); return retValue; } public void setImei(String imei) { checkDbState(); ContentValues cv = new ContentValues(); cv.put(GameColumns.IMEI, imei); mDb.update(GameColumns.TABLE, cv, null, null); } public boolean isOpen() { return mDb != null && mDb.isOpen(); } public void open() { mDbManager = new MyDatabaseManager(MyApplication.getApplication()); if (!isOpen()) { mDb = mDbManager.getWritableDatabase(); } } public void close() { if (isOpen()) { mDb.close(); mDb = null; if (mDbManager != null) { mDbManager.close(); mDbManager = null; } } } private void checkDbState() { if (mDb == null || !mDb.isOpen()) { throw new IllegalStateException("The database has not been opened"); } } private static class MyDatabaseManager extends SQLiteOpenHelper { private static final String DATABASE_NAME = "dbname"; private static final int DATABASE_VERSION = 7; private MyDatabaseManager(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { createGameTable(db); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + "!"); } private void dropDatabase(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + GameColumns.TABLE); } private void createGameTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + GameColumns.TABLE + " (" + GameColumns._ID + " INTEGER PRIMARY KEY," + GameColumns.IMEI + " TEXT," + GameColumns.LAST_UPDATE + " TEXT," + GameColumns.NICKNAME + " TEXT);"); ContentValues cv = new ContentValues(); cv.put(GameColumns.IMEI, "123456789012345"); cv.put(GameColumns.LAST_UPDATE, 0); cv.put(GameColumns.NICKNAME, (String) null); db.insert(GameColumns.TABLE, null, cv); } } } 

He tenido éxito con la creación de una clase base abstracta con la sentencia de nombre de base de datos / create y otra información compartida, y luego extenderla para cada tabla. De esta manera, puedo mantener todos mis métodos CRUD separados (lo que yo prefiero). El único inconveniente es que la instrucción DATABASE_CREATE debe residir en la clase padre y debe incluir todas las tablas, ya que no se pueden agregar nuevas tablas posteriormente, pero en mi opinión es un pequeño precio a pagar para mantener el CRUD Métodos para cada tabla separada.

Hacer esto fue bastante simple, pero he aquí algunas notas:

  • La instrucción create en la clase padre debe ser dividida para cada tabla, porque db.execSQL no puede ejecutar más de una sentencia.
  • Cambié todos los vars / métodos privados a protegidos, por si acaso.
  • Si está agregando tablas a una aplicación existente (no está seguro de si es específico para el emulador), debe desinstalar la aplicación y volver a instalarla.

Aquí está el código para mi clase de padre abstracto, que se basó en el tutorial de Bloc de notas. Los niños simplemente extienden esto, llamando al constructor del súper (siéntete libre de usar esto):

 package com.pheide.trainose; import android.content.Context; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public abstract class AbstractDbAdapter { protected static final String TAG = "TrainOseDbAdapter"; protected DatabaseHelper mDbHelper; protected SQLiteDatabase mDb; protected static final String TABLE_CREATE_ROUTES = "create table routes (_id integer primary key autoincrement, " + "source text not null, destination text not null);"; protected static final String TABLE_CREATE_TIMETABLES = "create table timetables (_id integer primary key autoincrement, " + "route_id integer, depart text not null, arrive text not null, " + "train text not null);"; protected static final String DATABASE_NAME = "data"; protected static final int DATABASE_VERSION = 2; protected final Context mCtx; protected static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(TABLE_CREATE_ROUTES); db.execSQL(TABLE_CREATE_TIMETABLES); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS routes"); onCreate(db); } } public AbstractDbAdapter(Context ctx) { this.mCtx = ctx; } public AbstractDbAdapter open() throws SQLException { mDbHelper = new DatabaseHelper(mCtx); mDb = mDbHelper.getWritableDatabase(); return this; } public void close() { mDbHelper.close(); } } 

Una explicación un poco más detallada está disponible aquí: http://pheide.com/page/11/tab/24#post13

La solución de phoxicle es un gran punto de partida, pero por las notas de Kevin Galligan en la serialización de SQLite de Android , esta implementación no es segura en el hilo y fallará silenciosamente cuando varias conexiones de base de datos intentan escribir la base de datos:

Si intenta escribir en la base de datos a partir de conexiones reales distintas al mismo tiempo, se producirá un error. No esperará hasta que se haga el primero y luego escriba. Simplemente no escribirá su cambio. Peor aún, si no llama a la versión correcta de insertar / actualizar en la base de datos SQLite, no obtendrá una excepción. Sólo obtendrá un mensaje en su LogCat, y eso será todo.

Entonces, ¿hilos múltiples? Utilice un ayudante.


Esta es una implementación modificada del adaptador de base de datos de phoxicles que usa una instancia estática de SQLiteOpenHelper y por lo tanto está limitada a una sola conexión de base de datos:

 public class DBBaseAdapter { private static final String TAG = "DBBaseAdapter"; protected static final String DATABASE_NAME = "db.sqlite"; protected static final int DATABASE_VERSION = 1; protected Context mContext; protected static DatabaseHelper mDbHelper; private static final String TABLE_CREATE_FOO = "create table foo (_id integer primary key autoincrement, " + "bar text not null)"); public DBBaseAdapter(Context context) { mContext = context.getApplicationContext(); } public SQLiteDatabase openDb() { if (mDbHelper == null) { mDbHelper = new DatabaseHelper(mContext); } return mDbHelper.getWritableDatabase(); } public void closeDb() { mDbHelper.close(); } protected static class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(TABLE_CREATE_FOO); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS routes"); onCreate(db); } } } 

Extienda DBBaseAdapter para cada tabla para implementar sus métodos CRUD:

 public class DBFooTable extends DBBaseAdapter { public DBFooTable(Context context) { super(context); } public void getBar() { SQLiteDatabase db = openDb(); // ... closeDb(); } 

Estoy un poco tarde tal vez pero siempre abrir mi base de datos, no mi mesa. Así que esto me forma como ningún sentido.

  firstTable.open(); secondTable.open(); 

Más bien haz esto.

  dataBase.getWritableDatabase(); 

Entonces si desea actualizar juste eligió la tabla:

 public int updateTotal (int id, Jours jour){ ContentValues values = new ContentValues(); values.put(COL_TOTAL,Total ); //update the table you want return bdd.update(TABLE_NAME, values, COL_JOUR + " = " + id, null); } 

Y eso es todo. Espero que pueda ayudar a otras personas

  • ¿Cómo pasar el adaptador de base de datos a otra actividad?
  • ¿Cómo encuentro el archivo de base de datos en un dispositivo virtual de Android?
  • SQLite lectura y escritura simultánea
  • Añadir un elemento a la lista de Firebase Databse
  • ActiveAndroid Excepción SQLite 'No such table'
  • Base de datos de diccionarios sin conexión para la aplicación de Android
  • SQLite Database: SQLiteDiskIOException: error de E / S de disco (código 1802)
  • ¿Cómo conectar el teléfono de android a un servicio de la tela funcionado en el servidor local?
  • Base de datos de Firebase de Android: Recuperación de datos únicos sin detector de eventos
  • La restricción UNIQUE falló: base de datos sqlite: android
  • "Tipo desconocido encontrado" al usar tipo genérico en Firebase GenericTypeIndicator
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.