Was wir brauchen:
Wir müssen in der Lage sein, die externen Datenbanken an die interne Raumdatenbank anzuhängen, ohne den Pfad zu ändern, in dem sie gespeichert sind. Nachdem wir sie hinzugefügt haben, möchten wir in der Lage sein, die Datenbanken mit den Raumentitäts-, Dao- und Datenbankobjekten zu verwenden. Gibt es eine Möglichkeit, dies zu erreichen?
Es ist möglicherweise einfacher, keine Verbindung herzustellen, da Sie eine separate Raumdatenbankinstanz verwenden könnten. Ohne müssten Sie separate DAOs haben, um den angehängten Schemanamen zu berücksichtigen (glaube ich). Sagen, dass das Beispiel unten (basierend auf etwas, mit dem ich für jemanden gespielt habe und daher die ziemlich verwirrenden Spaltennamen) .
z.B. unter der Annahme ATTACH DATABASE .... AS other
(das angehängte Schema ist other ) dann statt (für die Hauptdatenbank)
@Query("SELECT * FROM AllUsers")
List<AllUsers> getAllAllUsers();
Sie benötigen ein kostenloses :-
@SkipQueryVerification
@Query("SELECT * FROM other.AllUsers")
List<AllUsers> getOtherAllAllUsers();
usw.
Wenn Sie jedoch stattdessen so etwas wie (für die Hauptsache) hatten:-
mLPDB = Room.databaseBuilder(this,LoanPaymentDatabase.class,"mydb").allowMainThreadQueries().build();
mLPDB_DAO = mLPDB.mDao();
Zusammen mit (für den anderen) :-
mOtherDB = Room.databaseBuilder(this,LoanPaymentDatabase.class,OtherDatabaseHelper.DBNAME).allowMainThreadQueries().build();
mOtherDAO = mOtherDB.mDao();
Dann können Sie mit demselben DAO auf beide zugreifen.
- Hinweis natürlich wird davon ausgegangen, dass die Schemata komplementär (nicht unbedingt exakt) sind.
Nicht unbedingt genau?
Auch für den Kommentar :-
Sie müssen zuerst Daten in den Raum selbst migrieren.
-
Wenn Sie ein wenig spielen, können Sie die Migration umgehen indem Sie room täuschen, indem Sie user_version auf 0 setzen. In diesem Fall setzt Room die Versionsnummer (begrenzte Tests). Allerdings bin ich mir nicht sicher, was GreenDao oder Ihr Server davon halten würden (Ihre Hausaufgaben).
-
Meine begrenzten Tests betrafen ein häufiges Problem bei der Migration einer Spalte mit INTEGER PRIMARY KEY, dh ohne AUTOINCREMENT. Raum, wenn die Migration sich beschwert, dass das Schema nicht übereinstimmt. Also habe ich absichtlich nicht AUTOINCREMENT codiert, die user_version auf 0 gesetzt und keine Beschwerden beim Zugriff auf die Datenbank über Room. Verwendet auch einen Säulentyp von Rumplestilskin und keine Beschwerde.
Daher glaube ich, dass Sie Migrationsprobleme mit dem gefürchteten erwartet/gefunden umgehen können, indem Sie die user_version auf 0 setzen (und daher glaube ich, dass Sie die Migration umgehen). Offensichtlich müssen die Spaltennamen jedoch übereinstimmen, wenn sie in einer Entität definiert und nicht ignoriert werden.
- Ich habe auch versucht, eine nicht definierte Spalte für die Entität hinzuzufügen und die obigen Ergebnisse ohne Beanstandung zu verwenden (diese Tests sollten im folgenden Code offensichtlich sein).
Beispiel(e)
Das Folgende ist ein Beispiel für eine 2-Einheiten-Raum-Datenbank und zum Testen der anderen Datenbank eine, die außerhalb des Raums erstellt wurde und ausreichend mit der Raumdatenbank übereinstimmt, um verwendet werden zu können, d. h. Entitätsspaltennamen werden abgeglichen.
Die andere Datenbank
Die andere Nicht-Raum-Datenbank wird über einen SQLiteOpenHelper erstellt Unterklasse gemäß 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);
}
}
- siehe Kommentare für Kuriositäten/absichtlich hinzugefügte Unstimmigkeiten
- Dies ist gefüllt, wird von einer alternativen Raumdatenbank und über eine angehängte DB in MainActivity.java aufgerufen unten
Die Raumdatenbank
Die 2 Entitäten :-
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;
}
}
und 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;
}
}
Eine zusätzliche POJO-Klasse, AllUsersAndPaidUnpaidsList.java damit wurde gespielt und so eingebaut und benutzt :-
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());
}
}
Eine einzige Schnittstelle 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();
}
Die Raumdatenbankklasse LoanPaymentDatabase.java
@Database(entities = {AllUsers.class,PaidUnpaid.class},exportSchema = false,version = 1)
public abstract class LoanPaymentDatabase extends RoomDatabase {
public abstract Dao mDao();
}
Alles zusammenfügen
Endlich eine Aktivität, die :-
-
Erstellt und füllt das andere (Nicht-Raum-)Datenbank (falls nicht vorhanden) zum Testen. Setzen Sie die user_version (Datenbankversion in Android Talk) auf 0.
-
Erstellt bei Bedarf die Room-Version der Datenbank.
- Fügt ein paar Zeilen zur Room-Version hinzu.
- Gibt die Daten in der Room-Version an das Protokoll aus.
- Erzeugt eine alternative RoomDatabase unter Verwendung der anderen Datenbank.
- Gibt die Daten im anderen aus Datenbank über Room.
- Hängt das andere an Datenbank auf die Room-Version.
- Gibt die Daten von beiden über die ursprüngliche Raumdatenbank aus und greift auf die angehängte andere zu Datenbank über die zusätzlichen DAO-Schnittstellen, die other.???? beinhalten .
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();
}
}