Я читал учебник по Android-платформе Android SQLite, который ссылался на создание класса адаптера базы данных для создания и доступа к таблице БД. При работе с базами данных SQLite с несколькими таблицами лучше всего создать другой класс адаптера для каждой таблицы или создать один класс адаптера DB для всего приложения для Android?
Мое приложение использует несколько таблиц, и я надеялся, что не должен иметь один массивный класс адаптеров. проблема в том, что у меня есть вложенный подкласс SQLiteOpenHelper для примера NotePad в каждом адаптере. Когда открывается первая таблица, все работает нормально. Когда я пытаюсь получить доступ ко второй области (из-за другого действия), мое приложение вылетает.
Сначала я думал, что авария вызвана проблемой проверки версий, но оба адаптера теперь имеют одну и ту же версию базы данных и все еще сбой.
Вот пример одного из адаптеров DB для таблицы. Другие адаптеры соответствуют одному и тому же формату с различными реализациями.
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$
}
}
Когда первый адаптер, в этом случае userinfo, доступен, все работает так, как ожидалось. Скажем, у меня есть другой адаптер для информации о друге, который следует той же структуре, что и выше, когда к нему обращается другое действие, мне кажется, что вложенный подкласс SQLiteOpenHelper попытается снова создать базу данных. Очевидно, что-то не так, потому что в этом случае мое приложение вылетает.
Итак, стандартная практика в Android для создания одного адаптера мамонтового db вместо отдельных адаптеров для таблицы?
Вот решение, которое я в конечном итоге завершил. Это своего рода месиво из информации, полученной в книгах Commonsware, и некоторые вещи в Интернете, которые, как мне бы хотелось, я добавил в закладки, я хочу отдать должное:
Для каждого типа данных, которые мне нужно извлечь из db, я создаю класс "адаптер" (не подклассифицированный ничем). Эти классы адаптеров содержат все методы, необходимые для доступа к db для этой части информации. Например, если у меня было три таблицы в моем db:
У меня было бы три адаптера, которые выглядели бы так же, как и в следующем (я только ставил один в качестве демонстрации, но идея для всех одинакова):
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 For Beginners: создание нескольких таблиц SQLite для Android
Итак, мой главный DBAdapter (который отвечает за создание всех моих таблиц в одном db) выглядит так:
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);
}
}
Надеюсь, я помог.
ourConstructorBool
может быть true
если он не объявлен как «статический»?