複数のテーブルを操作するためのベストプラクティス
-
02-10-2019 - |
質問
アプリケーションに複数のテーブルを備えたデータベースを使用しています。解析中に2つのテーブルにデータを書き込む必要があるXMLパーサーがあります。両方のテーブル用に2つのデータベースアダプターを作成しましたが、今では問題があります。私が1つのテーブルで作業しているとき、それは簡単です:
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();
}
しかし、2番目のテーブルにデータを挿入する必要がある場合は、どうすればよいですか?たとえば、2番目のアダプターがある場合、これは悪い考えだと思います。
FirstDBAdapter firstTable = new FirstDBAdapter(mycontext);
SecondDBAdapter secondTable = new SecondDBAdapter(mycontext);
@Override
public void startDocument() throws SAXException
{
firstTable.open();
secondTable.open();
}
これを達成する方法について何か考えはありますか?
解決
私のデータベースアダプター。インスタンスは、アプリケーションから継承するMyApplicationに常に保存されます。最初のテーブルを定義した2番目のテーブルについて考えてみてください。現在、これは短いバージョンです。実際には、このアダプターはデータベース内の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メソッドを分離することができます(私はこれが非常に好きです)。唯一の欠点は、Database_Createステートメントが親クラスに存在する必要があり、すべてのテーブルを含める必要があることです。新しいテーブルは後で追加できないためですが、私の意見では、それはクラブを維持するために支払うためのわずかな価格です各テーブルのメソッドは分離します。
これを行うことはかなり簡単でしたが、ここにいくつかのメモがあります:
- 親クラスの作成ステートメント しなければならない db.execsqlは複数のステートメントを実行できないため、各テーブルの分割されます。
- 念のため、すべてのプライベートVARS/メソッドを保護されました。
- 既存のアプリケーションにテーブルを追加している場合(これがエミュレータに固有のかどうかはわかりません)、アプリケーションをアンインストールしてから再インストールする必要があります。
これは、メモ帳チュートリアルに基づいた私の抽象親クラスのコードです。子供たちは単にこれを拡張し、スーパーのコンストラクターを呼び出します(これを自由に使用してください):
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
Phoxicleの解決策は素晴らしい出発点ですが、Kevin Galliganによると AndroidのSQLiteシリアル化に関するメモ, 、この実装はスレッドが安全ではなく、複数のデータベース接続(異なるスレッドからの)がデータベースを作成しようとすると静かに失敗します。
実際の個別の接続から同時にデータベースに書き込もうとすると、失敗します。最初のものが完了するまで待ってから書くことはありません。それは単にあなたの変化を書くのではありません。さらに悪いことに、sqlitedatabaseで適切なバージョンの挿入/更新を呼び出さないと、例外が得られません。ログキャットにメッセージが表示されるだけです。
それで、複数のスレッド? 1つのヘルパーを使用します。
静的SQLiteOpenHelperインスタンスを使用しているため、単一のデータベース接続に制限されるPhoxicleのデータベースアダプターの変更された実装を次に示します。
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();
次に、Justを更新したい場合は、テーブルを選択しました。
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);
}
そして、それだけです。それが他の人を助けることができることを願っています