我在我的应用程序中使用包含多个表的数据库。我有一个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();
}
有关如何实现这一目标的任何想法?
答案 0 :(得分:31)
我成功创建了一个带有数据库名称/ create语句和其他共享信息的抽象基类,然后为每个表扩展它。这样,我可以将所有CRUD方法分开(我更喜欢)。唯一的缺点是DATABASE_CREATE语句必须驻留在父类中,并且必须包含所有表,因为之后不能添加新表,但在我看来,保留CRUD的代价很小每个表的方法分开。
这样做很简单,但这里有一些注意事项:
这是我的抽象父类的代码,它基于Notepad Tutorial。孩子们只是扩展这个,调用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
答案 1 :(得分:15)
我的数据库适配器。实例始终存储在从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);
}
}
}
答案 2 :(得分:10)
phoxicle的解决方案是一个很好的起点,但根据Kevin Galligan的notes on Android's SQLite serialization,这个实现不是线程安全的,当多个数据库连接(例如来自不同的线程)尝试编写数据库时,它将无声地失败:
如果您尝试同时从实际的不同连接写入数据库,则会失败。它不会等到第一个完成然后写。它根本不会写你的改变。更糟糕的是,如果您没有在SQLiteDatabase上调用正确版本的插入/更新,则不会出现异常。您只需在LogCat中收到一条消息即可。
那么,多线程?使用一个帮手。
以下是使用静态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();
}
答案 3 :(得分:1)
我有点迟了但是我总是打开我的数据库,而不是我的桌子。所以这对我来说毫无意义。
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);
}
就是这样。希望它可以帮助其他人