SQLite
 sql >> Base de données >  >> RDS >> SQLite

Room - Utilisation de SQLites externes ainsi que d'une base de données interne

Ce dont nous avons besoin :

Nous devons pouvoir utiliser attacher les bases de données externes à la Room-Database interne sans changer le chemin dans lequel elles sont stockées. Après les avoir ajoutées, nous voulons pouvoir utiliser les bases de données avec les objets Room Entity, Dao et Database. Existe-t-il un moyen d'y parvenir ?

Il pourrait être plus facile de ne pas attacher, la raison étant que vous pourriez utiliser une instance de base de données de salle distincte. Sans vous, vous auriez besoin d'avoir des DAO séparés pour répondre au nom de schéma joint (je crois). Dire que l'exemple ci-dessous (basé sur quelque chose avec lequel je jouais pour quelqu'un et donc les noms de colonnes plutôt déroutants) .

par exemple. en supposant ATTACH DATABASE .... AS other (le schéma joint étant autre ) puis à la place de (pour la base de données principale)

@Query("SELECT * FROM AllUsers")
List<AllUsers> getAllAllUsers();

Vous auriez besoin d'un :-

@SkipQueryVerification
@Query("SELECT * FROM other.AllUsers")
List<AllUsers> getOtherAllAllUsers();

etc.

Cependant, si vous aviez plutôt quelque chose comme (pour le principal) :-

    mLPDB = Room.databaseBuilder(this,LoanPaymentDatabase.class,"mydb").allowMainThreadQueries().build();
    mLPDB_DAO = mLPDB.mDao();

Avec (pour l'autre) :-

    mOtherDB = Room.databaseBuilder(this,LoanPaymentDatabase.class,OtherDatabaseHelper.DBNAME).allowMainThreadQueries().build();
    mOtherDAO = mOtherDB.mDao();

Ensuite, vous pouvez accéder aux deux en utilisant le même DAO.

  • Remarque bien sûr, ce qui précède suppose que les schémas sont complémentaires (pas nécessairement exacts).

Pas nécessairement exact ?

Couvrant également le commentaire :-

Vous devez d'abord migrer les données vers la salle elle-même.

  • En jouant un peu, vous pouvez contourner le besoin de migrer en trompant room en définissant user_version sur 0. Dans ce cas, Room définit le numéro de version (test limité). Cependant, je ne sais pas ce que GreenDao ou votre serveur en penseraient (vos devoirs).

  • Mes tests limités concernaient un problème courant lors de la migration, celui d'avoir une colonne avec INTEGER PRIMARY KEY, c'est-à-dire sans AUTOINCREMENT. Si vous migrez, Room se plaindra de l'incompatibilité du schéma. Donc je n'ai intentionnellement pas codé AUTOINCREMENT, mis le user_version à 0 et rien à redire en accédant à la base de données via Room. Également utilisé un type de colonne de rumplestilskin et aucune plainte.

En tant que tel, je pense que vous pouvez contourner les problèmes de migration avec le redoutable attendu/trouvé en définissant user_version sur 0 (et donc je pense contourner la migration). Évidemment, les noms de colonne doivent correspondre, s'ils sont définis dans une entité et non ignorés.

  • J'ai également essayé d'ajouter une colonne non définie à l'entité et d'utiliser les résultats ci-dessus sans me plaindre (ces tests devraient être évidents dans le code ci-dessous).

Exemple(s)

Ce qui suit est un exemple d'une base de données de 2 salles d'entités et à des fins de test de l'autre une base de données construite à l'extérieur de la salle qui correspond suffisamment à la base de données de la salle pour pouvoir être utilisée, c'est-à-dire que les noms de colonne d'entité sont mis en correspondance.

L'autre base de données

L'autre base de données non-room est créée via un SQLiteOpenHelper sous-classe selon OtherDatabaseHelper.java :-

public class OtherDatabaseHelper extends SQLiteOpenHelper {

    public static final String DBNAME = "lpolddb";
    public static final int DBVERSION = 1;
    public static final String ALLUSERS_TBL = "AllUsers";
    public static final String PAIDUNPAID_TBL = "PaidUnpaid";

    /*
        @PrimaryKey(autoGenerate = true)
        private long auid;
        private String Name;
        private int Loan;
        private int TimeInMonths;
     */
    public static final String ALLUSERS_COL_AUID = "auid";
    public static final String ALLUSERS_COL_NAME = "Name";
    public static final String ALLUSERS_COL_LOAN = "Loan";
    public static final String ALLUSERS_COL_TIMEINMONTHS = "TimeInMonths";

    private static final String crt_allusers_table_sql =
            "CREATE TABLE IF NOT EXISTS " + ALLUSERS_TBL + "(" +
                    //ALLUSERS_COL_AUID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
                    ALLUSERS_COL_AUID + " INTEGER PRIMARY KEY," +
                    ALLUSERS_COL_NAME + " TEXT, " +
                    ALLUSERS_COL_LOAN + " INTEGER, " +
                    "someothercolumnnotdefineinroom TEXT, " + //!!!!!!!!!! not a column in an entity
                    ALLUSERS_COL_TIMEINMONTHS + " INTEGER" +
                    ")";

    /*
        @PrimaryKey(autoGenerate = true)
        private long puid;
        private int TimeInMonths;
        private String PaidUnpaid;
        @ForeignKey(
            entity = AllUsers.class,
            parentColumns = {"auid"},
            childColumns = {"AllUsersReference"},
            onUpdate = ForeignKey.CASCADE, onDelete = ForeignKey.CASCADE)
        private long AllUsersReference;
     */

    public static final String PAIDUNPAID_COL_PUID = "puid";
    public static final String PAIDUNPAID_TIMEINMONTHS = ALLUSERS_COL_TIMEINMONTHS;
    public static final String PAIDUNPAID_COL_PAIDUNPAID = "PaidUnpaid";
    public static final String PAIDUNPAID_COL_ALLUSERSREFERENCE = "AllUsersReference";

    public static final String crt_paidunpaid_table_sql =
            "CREATE TABLE IF NOT EXISTS " + PAIDUNPAID_TBL + "(" +
                    PAIDUNPAID_COL_PUID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
                    PAIDUNPAID_TIMEINMONTHS + " rumplestilskin, " + // !!!!!!!!!!!
                    PAIDUNPAID_COL_PAIDUNPAID + " TEXT," +
                    PAIDUNPAID_COL_ALLUSERSREFERENCE + " INTEGER " +
                    " REFERENCES " + ALLUSERS_TBL + "(" + ALLUSERS_COL_AUID + ") " +
                    "ON UPDATE CASCADE ON DELETE CASCADE" +
                    ")";


    SQLiteDatabase mDB;
    public OtherDatabaseHelper(Context context) {
        super(context, DBNAME, null, DBVERSION);
        mDB = this.getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(crt_allusers_table_sql);
        db.execSQL(crt_paidunpaid_table_sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    public long insertAllUsers(String name, int loanamount, int periodofloan) {
        ContentValues cv = new ContentValues();
        cv.put(ALLUSERS_COL_NAME,name);
        cv.put(ALLUSERS_COL_LOAN,loanamount);
        cv.put(ALLUSERS_COL_TIMEINMONTHS,periodofloan);
        return mDB.insert(ALLUSERS_TBL,null,cv);
    }

    public long insertPaidUnpaid(int formonth, String status, long allUserreferenced) {
        ContentValues cv = new ContentValues();
        cv.put(PAIDUNPAID_TIMEINMONTHS,formonth);
        cv.put(PAIDUNPAID_COL_PAIDUNPAID,status);
        cv.put(PAIDUNPAID_COL_ALLUSERSREFERENCE,allUserreferenced);
        return mDB.insert(PAIDUNPAID_TBL,null,cv);
    }
}
  • voir les commentaires pour les bizarreries/écarts délibérément ajoutés
  • ceci est renseigné, accessible par une autre base de données de salles et via une base de données jointe dans MainActivity.java ci-dessous

La base de données des salles

Les 2 Entités :-

AllUsers.java

@Entity
public class AllUsers {
    @PrimaryKey(autoGenerate = true)
    private long auid;
    private String Name;
    private int Loan;
    private int TimeInMonths;

    public AllUsers() {
    }

    @Ignore
    public AllUsers(String Name, int Loan, int TimeInMonths) {
        this.Name = Name;
        this.Loan = Loan;
        this.TimeInMonths = TimeInMonths;
    }

    public long getAuid() {
        return auid;
    }

    public void setAuid(long auid) {
        this.auid = auid;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public int getLoan() {
        return Loan;
    }

    public void setLoan(int loan) {
        Loan = loan;
    }

    public int getTimeInMonths() {
        return TimeInMonths;
    }

    public void setTimeInMonths(int timeInMonths) {
        TimeInMonths = timeInMonths;
    }
}

et PaidUnpaid.java :-

@Entity
public class PaidUnpaid {
    @PrimaryKey(autoGenerate = true)
    private long puid;
    private int TimeInMonths;
    private String PaidUnpaid;
    @ForeignKey(
            entity = AllUsers.class,
            parentColumns = {"auid"},
            childColumns = {"AllUsersReference"},
            onUpdate = ForeignKey.CASCADE, onDelete = ForeignKey.CASCADE)
    private long AllUsersReference;


    public PaidUnpaid() {
    }

    @Ignore
    public PaidUnpaid(int TimeInMonths, String PaidUnpaid, long AllUsersreference) {
        this.TimeInMonths = TimeInMonths;
        this.PaidUnpaid = PaidUnpaid;
        this.AllUsersReference = AllUsersreference;
    }

    public long getPuid() {
        return puid;
    }

    public void setPuid(long puid) {
        this.puid = puid;
    }

    public int getTimeInMonths() {
        return TimeInMonths;
    }

    public void setTimeInMonths(int timeInMonths) {
        TimeInMonths = timeInMonths;
    }

    public String getPaidUnpaid() {
        return PaidUnpaid;
    }

    public void setPaidUnpaid(String paidUnpaid) {
        PaidUnpaid = paidUnpaid;
    }

    public long getAllUsersReference() {
        return AllUsersReference;
    }

    public void setAllUsersReference(long allUsersReference) {
        AllUsersReference = allUsersReference;
    }
}

Une classe POJO supplémentaire, AllUsersAndPaidUnpaidsList.java qui était joué avec ainsi incorporé et utilisé :-

public class AllUsersAndPaidUnpaidsList {

    @Embedded
    AllUsers allUsers;
    @Ignore
    @PrimaryKey
    long auid;

    @Ignore
    @Relation(entity = PaidUnpaid.class,parentColumn = "auid",entityColumn = "puid")
    List<PaidUnpaid> paidUnpaidList;

    @Ignore
    public AllUsersAndPaidUnpaidsList(AllUsers au, List<PaidUnpaid> pulist) {
        this.allUsers = au;
        this.paidUnpaidList = pulist;
    }

    public List<PaidUnpaid> getPaidUnpaidList() {
        return this.paidUnpaidList;
    }


    public void setPaidUnpaidList(List<PaidUnpaid> paidUnpaidList) {
        this.paidUnpaidList = paidUnpaidList;
    }

    public AllUsers getAllUsers() {
        return allUsers;
    }

    public void setAllUsers(AllUsers allUsers) {
        this.allUsers = allUsers;
    }

    public void outputToLog(String tag) {
        StringBuilder sb = new StringBuilder("AllUsersName = ")
                .append(this.allUsers.getName())
                .append(" TimeInMonths = ")
                .append(String.valueOf(this.allUsers.getTimeInMonths()))
                ;
        for (PaidUnpaid pu: this.getPaidUnpaidList()) {
            sb.append("\n\t TimeInMonths = ")
                    .append(String.valueOf(pu.getTimeInMonths()))
                    .append(" Paid/Unpaid = ")
                    .append(pu.getPaidUnpaid());
        }
        Log.d(tag,sb.toString());
    }
}

Une seule interface Dao.java :-

@androidx.room.Dao
public interface Dao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long[] insertAllUsers(AllUsers... allUsers);

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long insertAllUsers(AllUsers allUsers);

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long[] insertPaidUnpaid(PaidUnpaid... paidUnpaids);

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long insertPaidUnpaid(PaidUnpaid paidUnpaid);

    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updateAllUsers(AllUsers... allUsers);

    @Update(onConflict =  OnConflictStrategy.IGNORE)
    int updateAllUsers(AllUsers allUsers);

    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updatePaidUnpaid(PaidUnpaid... paidUnpaids);

    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updatePaidUnpaid(PaidUnpaid paidUnpaid);

    @Delete
    int deleteAllUsers(AllUsers... allUsers);

    @Delete
    int deleteAllUsers(AllUsers allUsers);

    @Delete
    int deletePaidUnpaid(PaidUnpaid... paidUnpaids);

    @Delete
    int deletePaidUnpaid(PaidUnpaid paidUnpaid);

    @Query("SELECT * FROM AllUsers")
    List<AllUsers> getAllAllUsers();

    @Query("SELECT * FROM AllUsers WHERE auid = :id")
    List<AllUsers> getOneAllUsersById(long id);

    @Query("SELECT * FROM PaidUnpaid")
    List<PaidUnpaid> getAllPaidUnpaids();

    @Query("SELECT * FROM PaidUnpaid WHERE puid = :id")
    List<PaidUnpaid> getOnePaidUnpaidById(long id);

    @Query("SELECT * FROM PaidUnpaid WHERE AllUsersReference = :allUsersid")
    List<PaidUnpaid> getPaidUnpaidsForAllUsersId(long allUsersid);

    /*************
     * Some Additional DAO's for attached not required for alternative helper
     * in practice you would likely need attached versions of all
     ************/

    @Query("SELECT * FROM other.PaidUnpaid WHERE AllUsersReference = :allUsersid")
    @SkipQueryVerification
    List<PaidUnpaid> getOtherPaidUnpaidForAllUsersId(long allUsersid);

    @SkipQueryVerification
    @Query("SELECT * FROM other.AllUsers")
    List<AllUsers> getOtherAllAllUsers();
}

La classe Room Database LoanPaymentDatabase.java

@Database(entities = {AllUsers.class,PaidUnpaid.class},exportSchema = false,version = 1)
public abstract class LoanPaymentDatabase extends RoomDatabase {
    public abstract Dao mDao();
}

Tout mettre ensemble

Enfin une activité qui :-

  1. Crée et remplit l'autre base de données (hors salle) (si elle n'existe pas) pour les tests. Réglage de user_version (version de la base de données dans Android Talk) sur 0.

  2. Crée, si besoin, la version Room de la base de données.

  3. Ajoute quelques lignes à la version Room.
  4. Sortie les données de la version Room dans le journal.
  5. Crée une RoomDatabase alternative en utilisant l'autre base de données.
  6. Envoie les données dans l'autre base de données via Room.
  7. Attache l'autre base de données à la version Room.
  8. Sortie les données des deux via la Roomdatabase d'origine en accédant à l'autre joint base de données via les interfaces DAO supplémentaires qui incluent other.???? .

MainActivity.java

public class MainActivity extends AppCompatActivity {

    LoanPaymentDatabase mLPDB;
    Dao mLPDB_DAO;

    LoanPaymentDatabase mOtherDB;
    Dao mOtherDAO;
    Random rnd = new Random();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        manageOtherDatabase();

        mLPDB = Room.databaseBuilder(this,LoanPaymentDatabase.class,"mydb").allowMainThreadQueries().build();
        mLPDB_DAO = mLPDB.mDao();
        // Add some(2) AllUsers
        mLPDB_DAO.insertAllUsers(new AllUsers("Fred",5000,5));
        mLPDB_DAO.insertAllUsers(new AllUsers("Mary", 4000,6));

        // Add Some PaidUnpaid's for each AllUsers
        // Random amount with random paid or unpaid
        // This is just for demonstration and doesn't reflect what would typically be done
        List<AllUsers> allusers =  mLPDB_DAO.getAllAllUsers();
        for (AllUsers au: allusers) {
            int lc = rnd.nextInt(4) + 1;
            int month = 1;
            for (int i=0; i < lc; i++) {
                String paid = "Paid";
                if((rnd.nextInt(2) % 2) > 0 ) {
                    paid = "Unpaid";
                }
                mLPDB_DAO.insertPaidUnpaid(new PaidUnpaid(month++, paid, au.getAuid()));
            }
        }

        //Extract all AllUsersAndPaidUnpaid (i.e  each AllUsers with the related PaidUnpaid for the AllUsers)
        ArrayList<AllUsersAndPaidUnpaidsList> aupulist = new ArrayList<>();
        for (AllUsers au: allusers) {
            List<PaidUnpaid> pulist = mLPDB_DAO.getPaidUnpaidsForAllUsersId(au.getAuid());
            aupulist.add(new AllUsersAndPaidUnpaidsList(au,pulist));
        }

        // Output the results
        for (AllUsersAndPaidUnpaidsList aupu: aupulist) {
            aupu.outputToLog("INITALAUPU");
        }

        //Use separate openHelper rather than ATTACH
        mOtherDB = Room.databaseBuilder(this,LoanPaymentDatabase.class,OtherDatabaseHelper.DBNAME).allowMainThreadQueries().build();
        mOtherDAO = mOtherDB.mDao();
        ArrayList<AllUsersAndPaidUnpaidsList> otheraupulist = new ArrayList<>();
        for (AllUsers oau: mOtherDAO.getAllAllUsers() ) {
            otheraupulist.add(new AllUsersAndPaidUnpaidsList(oau,mOtherDAO.getPaidUnpaidsForAllUsersId(oau.getAuid())));
        }
        for (AllUsersAndPaidUnpaidsList aupu: otheraupulist) {
            aupu.outputToLog("ALTDBAUPU");
        }

        // User Attach
        SupportSQLiteDatabase main_sdb = mLPDB.getOpenHelper().getWritableDatabase();
        SupportSQLiteDatabase other_sdb = mOtherDB.getOpenHelper().getWritableDatabase();
        main_sdb.execSQL("ATTACH DATABASE '" + other_sdb.getPath() + "' AS other");
        ArrayList<AllUsersAndPaidUnpaidsList> attachaupulist = new ArrayList<>();
        for (AllUsers aau: mLPDB_DAO.getAllAllUsers()) {
            attachaupulist.add(new AllUsersAndPaidUnpaidsList(aau,mLPDB_DAO.getPaidUnpaidsForAllUsersId(aau.getAuid())));
        }
        for (AllUsers aauother: mLPDB_DAO.getOtherAllAllUsers()) {
            attachaupulist.add(new AllUsersAndPaidUnpaidsList(aauother,mLPDB_DAO.getOtherPaidUnpaidForAllUsersId(aauother.getAuid())));
        }
        for (AllUsersAndPaidUnpaidsList aupu: attachaupulist) {
            aupu.outputToLog("ATTACHEDAUPU");
        }

        mLPDB.close();
    }

    /*********
     *  For testing purposes - Populate the OTHER database to be used
     *********/
    private void manageOtherDatabase() {
        OtherDatabaseHelper mODBHlpr = new OtherDatabaseHelper(this);
        SQLiteDatabase db = mODBHlpr.getWritableDatabase();
        db.execSQL("PRAGMA user_version = 0");
        if (DatabaseUtils.queryNumEntries(db,OtherDatabaseHelper.ALLUSERS_TBL) > 0) {
            db.close();
            mODBHlpr.close();
            return;
        }
        db.beginTransaction();
        for (int i= 0; i < 5; i++) {
            long auid = mODBHlpr.insertAllUsers("AU" + String.valueOf(i),10000 + 1,5 + i);
            for(int ii = 0; ii < 5; ii++) {
                mODBHlpr.insertPaidUnpaid(ii,"Paid",auid);
            }
        }
        db.setTransactionSuccessful();
        db.endTransaction();
        db.close();
        mODBHlpr.close();
    }
}