代码之家  ›  专栏  ›  技术社区  ›  Shawn

Android中的多表sqlite db适配器?

  •  60
  • Shawn  · 技术社区  · 14 年前

    我正在阅读android sqlite记事本教程,该教程引用了创建DB适配器类来创建和访问DB表。在处理多表的sqlite数据库时,是为每个表创建不同的适配器类,还是为整个Android应用程序创建单个DB适配器类?

    我的应用程序使用多个表,我希望不必有一个巨大的适配器类。但是,问题是,我在每个适配器中都有一个嵌套的sqliteOpenHelper子类,每个记事本示例都有一个。当访问第一个表时,一切都正常。当我尝试访问第二个tble(来自其他活动)时,我的应用程序崩溃。

    起初,我认为崩溃是由版本控制问题引起的,但是两个适配器现在都有相同的数据库版本,而且仍然崩溃。

    下面是表的一个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$
        }
    }
    

    当第一个适配器(在本例中是usersinfo)被访问时,一切都按预期工作。假设我有另一个朋友信息适配器,它遵循与上面相同的结构,当它被不同的活动访问时,在我看来,sqliteOpenHelper的嵌套子类将再次尝试创建数据库。显然是出了问题,因为在那种情况下,我的应用程序崩溃了。

    那么,Android中的标准做法是创建单个mammoth db适配器而不是每个表中的单个适配器吗?

    2 回复  |  直到 10 年前
        1
  •  80
  •   Shawn    13 年前

    下面是我最终实现的解决方案。这有点像是从公共软件书籍中获得的信息和一些我希望我在网络上做书签的东西混合而成的,因为我想给这些东西加上学分:

    对于我需要从数据库中提取的每种类型的数据,我创建了一个“适配器”类(不从任何子类化)。这些适配器类包含访问该信息段的数据库所需的所有方法。例如,如果数据库中有三个表:

    1. 汽车
    2. 小船
    3. 摩托车

    我将有三个类似于以下内容的适配器(我只将其中一个作为演示,但每个适配器的想法都是相同的):

    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: Creating multiple SQLite Tables for Android

    因此,我的主数据库适配器(负责在单个数据库中创建所有表)如下所示:

    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类,它的唯一职责是创建/升级表。对数据的所有其他访问都是通过单独的“适配器”类完成的。我发现这非常有效,并且不会产生我前面提到的版本控制问题。

    希望这有帮助。

        2
  •  9
  •   keyser help    12 年前

    由于同样的问题,我尝试了很多解决方案,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);
        }
    }
    

    希望我能帮忙。