Что нам нужно:
Нам нужно иметь возможность подключать внешние базы данных к внутренней Room-Database без изменения пути, в котором они хранятся. После добавлениямы хотим иметь возможность использовать базы данных с объектами Room Entity, Dao и Database.Есть ли какой-нибудь возможный способ добиться этого?
Возможно, проще не прикреплять, потому что вы можете использовать отдельный экземпляр Database Room.Без этого вам понадобятся отдельные DAO для обслуживания имени присоединенной схемы (я полагаю).Сказать, что приведенный ниже пример (основанный на чем-то, с чем я играл для кого-то и, следовательно, довольно запутанные имена столбцов) .
например, предполагая ATTACH DATABASE .... AS other
(присоединенная схема other) затем вместо (для основной базы данных)
@Query("SELECT * FROM AllUsers")
List<AllUsers> getAllAllUsers();
Вам понадобится бесплатный: -
@SkipQueryVerification
@Query("SELECT * FROM other.AllUsers")
List<AllUsers> getOtherAllAllUsers();
и т. Д.
Однако, если вывместо этого было что-то вроде (для основного): -
mLPDB = Room.databaseBuilder(this,LoanPaymentDatabase.class,"mydb").allowMainThreadQueries().build();
mLPDB_DAO = mLPDB.mDao();
Наряду с (для другого): -
mOtherDB = Room.databaseBuilder(this,LoanPaymentDatabase.class,OtherDatabaseHelper.DBNAME).allowMainThreadQueries().build();
mOtherDAO = mOtherDB.mDao();
Тогда вы можете получить доступ к обоим, используя один и тот же DAO.
- Примечание Конечно, вышеизложенное предполагает, что схема является дополнительной (не обязательно точной).
Не обязательно точной?
Такжепокрытие комментария: -
Вы должны сначала перенести данные в саму комнату.
Немного поиграв, вы можете обойти необходимость перенести , обманывая комнату, устанавливая user_version в 0. В этом случае Room устанавливает номер версии (ограничениебуду тестировать).Однако я не уверен, что GreenDao или ваш сервер будут делать с этим (ваша домашняя работа).
Мое ограниченное тестирование было связано с распространенной проблемой при переносе колонки с INTEGER.ПЕРВИЧНЫЙ КЛЮЧ, т.е. без АВТОМАТИЧЕСКИХКомната при миграции будет жаловаться на несоответствие схемы.Поэтому я намеренно не кодировал AUTOINCREMENT, установил для user_version значение 0 и никаких жалоб на доступ к базе данных через Room.Также использовался тип столбца rumplestilskin и никаких жалоб.
Таким образом, я считаю, что вы можете обойти проблемы миграции с ужасной ожидаемой / найденной установкой user_versionдо 0 (и, следовательно, я считаю, обойти миграцию).Очевидно, что хотя имена столбцов должны совпадать, если они определены в сущности и не игнорируются.
- Я также попытался добавить столбец, не определяющий сущность, и использовать приведенные выше результаты без жалоб (эти тестыдолжно быть очевидно в приведенном ниже коде.)
Пример (ы)
Ниже приведен пример базы данных 2 Entity Room и для целей тестирования другой база данных, созданная за пределами помещения, которая в достаточной степени соответствует базе данных помещения, чтобы можно было использовать ее, т. Е. Имена столбцов сущностей совпадают.
Другая база данных
Другая база данных без номеровсозданный с помощью SQLiteOpenHelper подкласса согласно 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);
}
}
- см. комментарии о странностях / целенаправленно добавленных несоответствиях
- это заполнено, доступно через альтернативную базу данных Room и через присоединенную базу данных в MainActivity.java ниже
База данных Room
2 сущности: -
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;
}
}
и 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;
}
}
дополнительный класс POJO, AllUsersAndPaidUnpaidsList.java , в который играли с таким включением и использованием: -
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());
}
}
Единый интерфейс 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();
}
Класс комнаты базы данных LoanPaymentDatabase.java
@Database(entities = {AllUsers.class,PaidUnpaid.class},exportSchema = false,version = 1)
public abstract class LoanPaymentDatabase extends RoomDatabase {
public abstract Dao mDao();
}
Собираем все вместе
Наконец, действие, которое: -
Создает и заполняет другую (не комнату) базу данных (если она не существует) для тестирования. Установка user_version (версия базы данных в Android Talk) на 0.
Создает, если необходимо, версию базы данных Room.
- Добавляет пару строк в версию Room.
- Выводит данные из версии комнаты в журнал.
- Создает альтернативную базу данных RoomDatabase с использованием other database.
- Выводит данные в базу данных other через Room.
- Присоединяет базу данных other к версии Room.
- Выводит данные из обоих через исходную базу данных Roomd, получая доступ к присоединенной базе данных other через дополнительные интерфейсы DAO, которые включают 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();
}
}