60 votes

Adaptateur(s) de base de données SQLite à tables multiples dans Android ?

Je lisais le tutoriel Android SQLite NotePad qui faisait référence à la création d'une classe DB Adapter pour créer et accéder à une table DB. Dans le cas d'une base de données SQLite à plusieurs tables, est-il préférable de créer une classe d'adaptateur différente pour chaque table ou de créer une classe d'adaptateur DB unique pour l'ensemble de l'application Android ?

Mon application utilise plusieurs tables et j'espérais ne pas avoir à avoir une seule classe d'adaptateur massive. Le problème, cependant, est que j'ai une sous-classe imbriquée de SQLiteOpenHelper selon l'exemple NotePad dans chaque adaptateur. Lorsque l'on accède à la première table, tout fonctionne bien. Lorsque j'essaie ensuite d'accéder à la deuxième table (à partir d'une activité différente), mon application se bloque.

Au début, je pensais que le plantage était dû à un problème de version, mais les deux adaptateurs ont maintenant la même version de base de données et le plantage persiste.

Voici un exemple de l'un des adaptateurs DB pour la table. Les autres adaptateurs suivent tous le même format avec des implémentations différentes.

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$
    }
}

Lorsque l'on accède au premier adaptateur, dans ce cas usersinfo, tout fonctionne comme prévu. Disons que j'ai un autre adaptateur pour les informations sur les amis qui suit la même structure que ci-dessus, lorsqu'il est accédé par une activité différente, il me semble que la sous-classe imbriquée de SQLiteOpenHelper devrait essayer de créer la base de données à nouveau. Il est évident que quelque chose ne va pas, car dans ce scénario, mon application se plante.

Est-ce que la pratique standard dans Android est de créer un seul adaptateur de base de données mammouth au lieu d'adaptateurs individuels par table ?

80voto

Shawn Points 1407

Voici la solution que j'ai fini par mettre en œuvre. Il s'agit d'un mélange d'informations tirées des livres sur le Commonsware et d'informations trouvées sur le Web que j'aimerais avoir mises en signet pour pouvoir les citer :

Pour chaque type de données que je dois extraire de la base de données, je crée une classe "adaptateur" (qui n'est pas une sous-classe de quoi que ce soit). Ces classes d'adaptateurs contiennent toutes les méthodes nécessaires pour accéder à la base de données pour ce type d'information. Par exemple, si j'ai trois tables dans ma base de données :

  1. Voitures
  2. Bateaux
  3. Motos

J'aurais trois adaptateurs qui ressembleraient à ce qui suit (je n'en mets qu'un pour la démonstration, mais l'idée est la même pour chacun) :

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

}

Donc si vous imaginez que j'ai une de ces classes "adaptateurs" pour chaque table.

Lorsque l'écran d'accueil de mon application démarre, j'utilise la technique suivante Android pour les débutants : Création de tables SQLite multiples pour Android

Mon adaptateur de base de données principal (qui est responsable de la création de toutes mes tables dans une seule base de données) ressemble donc à ceci :

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

La classe DBAdapter n'est appelée qu'au premier démarrage de l'application et sa seule responsabilité est de créer/mettre à jour les tables. Tout autre accès aux données est effectué par la classe "adaptateur" individuelle. J'ai constaté que cela fonctionne parfaitement et ne crée pas les problèmes de versionnage que j'ai mentionnés précédemment.

J'espère que cela vous aidera.

9voto

Asaf Points 71

J'ai eu le même problème, j'ai essayé plusieurs solutions, finalement j'ai créé une méthode abstraite qui construit la structure de la base de données et une classe étendue pour les classes de tables.

C'est ma classe de construction de base de données et elle est abstraite :

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

Et voici l'une de mes classes de table, les autres classes sont implémentées de la même manière, ajoutez-en autant que vous le souhaitez :

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

J'espère avoir aidé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X