Несколько таблиц SQLite DB Adapter (S) В Android?
Я читал учебник Android SQLite NotePad, который ссылался на создание класса адаптера БД для создания и доступа к таблице БД. При работе с многотабличной базой данных SQLite рекомендуется создать другой класс адаптера для каждой таблицы или создать один класс адаптера DB для всего приложения Android?
мое приложение использует несколько таблиц, и я надеялся, что не придется иметь один массивный класс адаптера. проблема, однако, в том, что у меня есть вложенные подкласс класса SQLiteOpenHelper на примере Блокнота в пределах каждого адаптера. Когда первая таблица доступна, все работает нормально. Когда я затем пытаюсь получить доступ ко второму tble (из другой деятельности), мое приложение аварийно завершает работу.
сначала я думал, что сбой вызван проблемой управления версиями, но оба адаптера теперь имеют одну и ту же версию базы данных, и она все еще сбой.
вот пример одного из адаптеров БД для таблицы. Все остальные адаптеры следуют тот же формат с различными реализациями.
public class InfoDBAdapter {
public static final String ROW_ID = "_id";
public static final String NAME = "name";
private static final String TAG = "InfoDbAdapter";
private static final String DATABASE_NAME = "myappdb";
private static final String DATABASE_TABLE = "usersinfo";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_CREATE = "create table usersinfo (_id integer primary key autoincrement, "
+ NAME
+ " TEXT," + ");";
private DatabaseHelper mDbHelper;
private SQLiteDatabase mDb;
private final Context mCtx;
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " //$NON-NLS-1$//$NON-NLS-2$
+ newVersion + ", which will destroy all old data"); //$NON-NLS-1$
//db.execSQL("DROP TABLE IF EXISTS usersinfo"); //$NON-NLS-1$
onCreate(db);
}
}
public InfoDBAdapter(Context ctx) {
this.mCtx = ctx;
}
public InfoDBAdapter open() throws SQLException {
this.mDbHelper = new DatabaseHelper(this.mCtx);
this.mDb = this.mDbHelper.getWritableDatabase();
return this;
}
/**
* close return type: void
*/
public void close() {
this.mDbHelper.close();
}
public long createUser(String name) {
ContentValues initialValues = new ContentValues();
initialValues.put(NAME, name);
return this.mDb.insert(DATABASE_TABLE, null, initialValues);
}
public boolean deleteUser(long rowId) {
return this.mDb.delete(DATABASE_TABLE, ROW_ID + "=" + rowId, null) > 0; //$NON-NLS-1$
}
public Cursor fetchAllUsers() {
return this.mDb.query(DATABASE_TABLE, new String[] { ROW_ID,
NAME}, null, null, null, null, null);
}
public Cursor fetchUser(long rowId) throws SQLException {
Cursor mCursor =
this.mDb.query(true, DATABASE_TABLE, new String[] { ROW_ID, NAME}, ROW_ID + "=" + rowId, null, //$NON-NLS-1$
null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
public boolean updateUser(long rowId, String name) {
ContentValues args = new ContentValues();
args.put(NAME, name);
return this.mDb
.update(DATABASE_TABLE, args, ROW_ID + "=" + rowId, null) > 0; //$NON-NLS-1$
}
}
при обращении к первому адаптеру, в данном случае usersinfo, все работает так, как ожидалось. Предположим, у меня есть другой адаптер для информации о друзьях, который следует той же структуре, что и выше, когда к нему обращается другая активность, мне кажется, что вложенный подкласс SQLiteOpenHelper попытается снова создать базу данных. Явно что-то не так, потому что в этом случае мое приложение крахи.
Итак, стандартная практика в Android заключается в создании одного адаптера Mammoth db вместо отдельных адаптеров для каждой таблицы?
2 ответа:
вот решение, которое я в конечном итоге реализовал. Это своего рода месиво из информации, полученной в книгах Commonsware, и некоторые вещи в интернете, которые я хотел бы отметить, потому что я хочу отдать должное:
для каждого типа данных, которые мне нужно извлечь из БД, я создаю класс "адаптер" (не подкласс из ничего). Эти классы адаптеров содержат все методы, необходимые для доступа к БД для этой части информации. Например, если бы у меня было три таблицы в моем дБ:
- - автомобили
- лодки
- мотоциклы
у меня было бы три адаптера, которые выглядели бы примерно так(я только помещаю один в качестве демо, но идея одинакова для каждого):
public class CarsDBAdapter { public static final String ROW_ID = "_id"; public static final String NAME = "name"; public static final String MODEL = "model"; public static final String YEAR = "year"; private static final String DATABASE_TABLE = "cars"; private DatabaseHelper mDbHelper; private SQLiteDatabase mDb; private final Context mCtx; private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DBAdapter.DATABASE_NAME, null, DBAdapter.DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } /** * Constructor - takes the context to allow the database to be * opened/created * * @param ctx * the Context within which to work */ public CarsDBAdapter(Context ctx) { this.mCtx = ctx; } /** * Open the cars database. If it cannot be opened, try to create a new * instance of the database. If it cannot be created, throw an exception to * signal the failure * * @return this (self reference, allowing this to be chained in an * initialization call) * @throws SQLException * if the database could be neither opened or created */ public CarsDBAdapter open() throws SQLException { this.mDbHelper = new DatabaseHelper(this.mCtx); this.mDb = this.mDbHelper.getWritableDatabase(); return this; } /** * close return type: void */ public void close() { this.mDbHelper.close(); } /** * Create a new car. If the car is successfully created return the new * rowId for that car, otherwise return a -1 to indicate failure. * * @param name * @param model * @param year * @return rowId or -1 if failed */ public long createCar(String name, String model, String year){ ContentValues initialValues = new ContentValues(); initialValues.put(NAME, name); initialValues.put(MODEL, model); initialValues.put(YEAR, year); return this.mDb.insert(DATABASE_TABLE, null, initialValues); } /** * Delete the car with the given rowId * * @param rowId * @return true if deleted, false otherwise */ public boolean deleteCar(long rowId) { return this.mDb.delete(DATABASE_TABLE, ROW_ID + "=" + rowId, null) > 0; //$NON-NLS-1$ } /** * Return a Cursor over the list of all cars in the database * * @return Cursor over all cars */ public Cursor getAllCars() { return this.mDb.query(DATABASE_TABLE, new String[] { ROW_ID, NAME, MODEL, YEAR }, null, null, null, null, null); } /** * Return a Cursor positioned at the car that matches the given rowId * @param rowId * @return Cursor positioned to matching car, if found * @throws SQLException if car could not be found/retrieved */ public Cursor getCar(long rowId) throws SQLException { Cursor mCursor = this.mDb.query(true, DATABASE_TABLE, new String[] { ROW_ID, NAME, MODEL, YEAR}, ROW_ID + "=" + rowId, null, null, null, null, null); if (mCursor != null) { mCursor.moveToFirst(); } return mCursor; } /** * Update the car. * * @param rowId * @param name * @param model * @param year * @return true if the note was successfully updated, false otherwise */ public boolean updateCar(long rowId, String name, String model, String year){ ContentValues args = new ContentValues(); args.put(NAME, name); args.put(MODEL, model); args.put(YEAR, year); return this.mDb.update(DATABASE_TABLE, args, ROW_ID + "=" + rowId, null) >0; } }
Итак, если вы представляете, что у меня есть один из этих классов "адаптеров" для каждой таблицы.
когда мой экран заставки приложения запускается, я использую технику, представленную Android для начинающих: создание нескольких таблиц SQLite для Android
Итак, мой основной DBAdapter (который отвечает за создание всех моих таблиц в одной БД) выглядит так:
public class DBAdapter { public static final String DATABASE_NAME = "stuffIOwn"; //$NON-NLS-1$ public static final int DATABASE_VERSION = 1; private static final String CREATE_TABLE_CARS = "create table cars (_id integer primary key autoincrement, " //$NON-NLS-1$ + CarsDBAdapter.NAME+ " TEXT," //$NON-NLS-1$ + CarsDBAdapter.MODEL+ " TEXT," //$NON-NLS-1$ + CarsDBAdapter.YEAR+ " TEXT" + ");"; //$NON-NLS-1$ //$NON-NLS-2$ private static final String CREATE_TABLE_BOATS = "create table boats (_id integer primary key autoincrement, " //$NON-NLS-1$ +BoatsDBAdapter.NAME+" TEXT," //$NON-NLS-1$ +BoatsDBAdapter.MODEL+" TEXT," //$NON-NLS-1$ +BoatsDBAdapter.YEAR+" TEXT"+ ");"; //$NON-NLS-1$ //$NON-NLS-2$ private static final String CREATE_TABLE_CYCLES = "create table cycles (_id integer primary key autoincrement, " //$NON-NLS-1$ +CyclesDBAdapter.NAME+" TEXT," //$NON-NLS-1$ +CyclesDBAdapter.MODEL+" TEXT," //$NON-NLS-1$ +CyclesDBAdapter.YEAR+" TEXT"+ ");"; //$NON-NLS-1$ //$NON-NLS-2$ private final Context context; private DatabaseHelper DBHelper; private SQLiteDatabase db; /** * Constructor * @param ctx */ public DBAdapter(Context ctx) { this.context = ctx; this.DBHelper = new DatabaseHelper(this.context); } private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_CARS); db.execSQL(CREATE_TABLE_BOATS); db.execSQL(CREATE_TABLE_CYCLES); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // Adding any table mods to this guy here } } /** * open the db * @return this * @throws SQLException * return type: DBAdapter */ public DBAdapter open() throws SQLException { this.db = this.DBHelper.getWritableDatabase(); return this; } /** * close the db * return type: void */ public void close() { this.DBHelper.close(); } }
класс DBAdapter вызывается только при первом запуске приложения, и его единственной обязанностью является создание/обновление таблиц. Весь остальной доступ к данным осуществляется через отдельный класс "адаптер". Я обнаружил, что это работает отлично и не создает проблем с версиями, о которых я упоминал ранее.
надеюсь, что это помогает.
была та же проблема, пробовал много решений, finnaly я сделал абстрактный метод, который строит структуру базы данных и имеет расширенный класс для табличных классов.
Это мой класс конструктора базы данных и является абстрактным:
public abstract class dbAdapter { public static String DATABASE_NAME = ""; public static final int DATABASE_VERSION = 1; public static final String DATABASE_TABLE1 = "ContactName"; public static final String DATABASE_TABLE2 = "PhoneNumber"; public static DbHelper ourHelper; public static Context ourContext; public static SQLiteDatabase ourDatabase; boolean ourConstructorBool = false; boolean ourDB = false; public static final String ContactNameTable = "CREATE TABLE "+DATABASE_TABLE1+" (" + ContactNameAdapter.KEY_ROWID+" INTEGER PRIMARY KEY AUTOINCREMENT, " + ContactNameAdapter.KEY_NAME+" TEXT, " + ContactNameAdapter.KEY_BIRTH_DATE+" TEXT);"; public static final String PhoneNumberTable = "CREATE TABLE "+DATABASE_TABLE2+" (" + PhoneNumberAdapter.KEY_NUMBER+" TEXT , " + PhoneNumberAdapter.KEY_DESCRIPTION+" TEXT, " + PhoneNumberAdapter.KEY_CONTACTID+" TEXT, " + "FOREIGN KEY(" + PhoneNumberAdapter.KEY_CONTACTID +") REFERENCES " + (ContactNameAdapter.DATABASE_TABLE)+"("+ContactNameAdapter.KEY_ROWID+") ON DELETE CASCADE"+ ");"; static class DbHelper extends SQLiteOpenHelper{ public DbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(ContactNameTable); db.execSQL(PhoneNumberTable); } @Override public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) { db.execSQL("DROP TABLE IF EXISTS " + ContactNameAdapter.DATABASE_TABLE); db.execSQL("DROP TABLE IF EXISTS " + PhoneNumberAdapter.DATABASE_TABLE); onCreate(db); } } public dbAdapter(Activity a){ if(!ourConstructorBool == true){ ourContext = a; DATABASE_NAME = a.getString(Asaf.com.contactsEX.R.string.DB_NAME); ourConstructorBool = true; } } public dbAdapter open() throws SQLException{ if(!ourDB == true){ ourHelper = new DbHelper(ourContext); ourDB = true; } ourDatabase = ourHelper.getWritableDatabase(); return this; } public void close(){ if(ourDatabase.isOpen()) ourHelper.close(); } }
и это один из моих классов таблиц, остальные классы реализованы так же, просто добавьте столько, сколько вам нравится:
public class PhoneNumberAdapter extends dbAdapter{ public static final String KEY_NUMBER = "PhoneNumber"; public static final String KEY_DESCRIPTION = "Description"; public static final String KEY_CONTACTID = "ContactName_id"; public static final String DATABASE_TABLE = "PhoneNumber"; public PhoneNumberAdapter(Activity a){ super(a); } public long createEntry(String number, String description,long id){ // TODO Auto-generated method stub ContentValues cv = new ContentValues(); cv.put(KEY_NUMBER, number); cv.put(KEY_DESCRIPTION, description); cv.put(KEY_CONTACTID, id); return ourDatabase.insert(DATABASE_TABLE, null,cv); } }
надеюсь, что помогла.