使用多个表的最佳做法

我在我的应用程序中使用具有多个表的数据库。 我有一个XMLparsing器,它需要在parsing时将数据写入两个表。 我为这两个表创build了两个数据库适配器,但是现在我遇到了问题。 当我使用一张桌子时,很容易:

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

由于这是一个SAXparsing器,在我看来(也许我错了),这将会更好:

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

任何想法如何实现这一目标?

Solutions Collecting From Web of "使用多个表的最佳做法"

我的数据库适配器 一个实例总是存储在inheritance自Application的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); } } } 

我用数据库名称/创build语句和其他共享信息创build了抽象基类,然后对每个表进行扩展,都取得了成功。 这样,我可以保持我所有的CRUD方法(我更喜欢)。 唯一的缺点是DATABASE_CREATE语句必须驻留在父类中,并且必须包含所有的表,因为之后不能添加新的表,但是在我看来,保留CRUD是一个小的代价每个表的方法是分开的。

这样做相当简单,但是这里有一些注意事项:

  • 父类中的create语句必须分解为每个表,因为db.execSQL不能执行多个语句。
  • 为了以防万一,我改变了所有的私人variables/方法来保护。
  • 如果要将表添加到现有的应用程序(不确定这是否特定于模拟器),则应用程序必须先卸载然后重新安装。

这里是我的抽象父类的代码,这是基于记事本教程。 孩子们简单地扩展它,调用超级的构造函数(随意使用这个):

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

稍微更详细的解释可以在这里find: http : //pheide.com/page/11/tab/24#post13

phoxicle的解决scheme是一个很好的起点,但是根据Kevin Galligan 关于Android的SQLite序列化的笔记 ,这个实现不是线程安全的,当多个数据库连接(例如来自不同线程)试图写入数据库时​​,

如果您尝试从实际不同的连接同时写入数据库,则会失败。 它不会等到第一个完成,然后写。 它不会写你的改变。 更糟的是,如果你没有在SQLiteDatabase上调用正确版本的insert / update,你将不会得到exception。 你会在你的LogCat中得到一个消息,就是这样。

那么,multithreading? 使用一个帮手。


下面是一个使用静态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(); 

那么如果你想更新理由select了表格:

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

就这样。 希望它可以帮助别人