Рекомендации по работе с несколькими таблицами

Я использую базу данных с несколькими таблицами в своем приложении. У меня есть синтаксический анализатор XML, который должен записывать данные в две таблицы при разборе. Я создал два адаптера баз данных для обеих таблиц, но теперь у меня есть проблема. Когда я работаю с одним столом, это легко:

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(); 

Так как это SAX-парсер, на мой взгляд (может быть, я ошибаюсь), это будет еще лучше:

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();
}

Но как мне это сделать, если мне нужно вставить данные во вторую таблицу? Например, если у меня есть второй адаптер, который, я думаю, будет плохой идеей:

FirstDBAdapter firstTable = new FirstDBAdapter(mycontext);
SecondDBAdapter secondTable = new SecondDBAdapter(mycontext);

@Override
public void startDocument() throws SAXException 
{
    firstTable.open();
    secondTable.open(); 
}

Любые мысли о том, как достичь этого?

Ответ 1

Мой адаптер базы данных. Экземпляр всегда хранится в MyApplication, который наследуется от приложения. Подумайте о второй таблице, где я определил первую... в настоящее время это всего лишь короткая версия, на самом деле этот адаптер обрабатывает 7 таблиц в базе данных.

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);
        }
    }
}

Ответ 2

У меня был успех с созданием абстрактного базового класса с оператором имени/создания базы данных и другой общей информацией, а затем расширением его для каждой таблицы. Таким образом, я могу отделить все мои методы CRUD (что я предпочитаю). Единственным недостатком является то, что оператор DATABASE_CREATE должен находиться в родительском классе и должен включать все таблицы, поскольку после этого новые таблицы не могут быть добавлены, но, на мой взгляд, небольшая цена, чтобы заплатить за сохранение CRUD методы для каждой отдельной таблицы.

Делать это было довольно просто, но вот несколько заметок:

  • Оператор create в родительском классе должен быть разбит для каждой таблицы, так как db.execSQL не может выполнять более одного оператора.
  • Я изменил все частные vars/методы на защищенный, на всякий случай.
  • Если вы добавляете таблицы в существующее приложение (не уверен, что это специфично для эмулятора), приложение необходимо удалить, а затем переустановить.

Вот код для моего абстрактного родительского класса, который был основан на учебнике Notepad. Дети просто расширяют это, вызывая супер конструктор (не стесняйтесь использовать это):

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();
    }

}

Несколько более подробное объяснение доступно здесь: http://pheide.com/page/11/tab/24#post13

Ответ 3

phoxicle solution - отличная отправная точка, но в примечаниях к сериализации Android SQLite в отношении сериализации Android SQLite эта реализация не является потокобезопасной и будет терпеть неудачу, когда несколько соединений с базой данных (например, из разных потоков) попробуйте написать базу данных:

Если вы попытаетесь одновременно записать в базу данных из реальных отдельных соединений, произойдет сбой. Он не будет ждать, пока первое не будет сделано, а затем напишет. Он просто не напишет ваши изменения. Хуже того, если вы не вызываете правильную версию вставки/обновления в SQLiteDatabase, вы не получите исключение. Вы просто получите сообщение в своем LogCat, и это будет оно.

Итак, несколько потоков? Используйте одного помощника.


Здесь представлена ​​модифицированная реализация адаптера базы данных phoxicle, которая использует статический экземпляр SQLiteOpenHelper и, таким образом, ограничивается единственным подключением к базе данных:

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);
        }
    }
}

Расширьте DBBaseAdapter для каждой таблицы, чтобы реализовать ваши методы CRUD:

public class DBFooTable extends DBBaseAdapter {

    public DBFooTable(Context context) {
        super(context);
    }

    public void getBar() {

        SQLiteDatabase db = openDb();
        // ...
        closeDb();
}

Ответ 4

Я немного опаздываю, но я всегда открываю свою базу данных, а не свою таблицу. Так что это не меняет смысл.

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

Скорее сделайте это.

    dataBase.getWritableDatabase();

то если вы хотите обновить juste, выберите таблицу:

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);
}

И это все. Надеюсь, что это поможет другим людям.