Лучшие практики для работы с несколькими таблицами

StackOverflow https://stackoverflow.com/questions/3684678

Вопрос

Я использую базу данных с несколькими таблицами в моем приложении. У меня есть анализатор 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(); 

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

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

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

Это было полезно?

Решение

Мой адаптер базы данных. Экземпляр всегда хранится в 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);
        }
    }
}

Другие советы

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

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

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

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

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.

Решение сфокилы - отличная отправная точка, но на Кевин Галлиган Примечания на SQLite Seclite Android, эта реализация не безопасна в потоке и не будет работать молча, когда несколько подключений к базе данных (например, из разных потоков) пытаются записать базу данных:

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

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


Вот модифицированная реализация адаптера базы данных сфокула, который использует статический экземпляр 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();
}

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

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

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

    dataBase.getWritableDatabase();

Тогда, если вы хотите обновить Nasse, выбрал таблицу:

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top