Комната - Использование внешних SQLite, а также внутренней БД

В нашей компании в настоящее время мы рассматриваем возможность перехода на Room для управления базами данных наших мобильных устройств. Основные причины, по которым мы хотим это сделать, — поддержка наследования и повышенная тестируемость.

В настоящее время мы используем GreenDao для доступа к внутренней базе данных (в которой хранятся данные сеанса, настройки, пользовательские данные и тому подобное) и для доступа к нашим внешним базам данных (sqlites, которые мы извлекаем с FTP-сервера и храним в подпапках в /sdcard/Android/app-folder/ ; они предоставляют данные, которые мы используем в нашей бизнес-логике).

Что нам нужно: Нам нужно иметь возможность подключать внешние базы данных к внутренней базе данных Room без изменения пути, в котором они хранятся. После их добавления мы хотим иметь возможность использовать базы данных с объектами Room Entity, Dao и Database. Есть ли способ достичь этого?

Комнатная версия: 1.1.1 (вспомогательные библиотеки)

Уровень API: 15

Сначала вы должны перенести данные в саму комнату.

Sushant Somani 07.06.2019 15:35

@SushantSomani, поэтому нам пришлось бы переходить на sqlite-базы данных в базы данных комнат каждый раз, когда они обновляются (обычно примерно раз в месяц, иногда ежедневно, зависит от того, сколько ошибок внедрила команда, которая их создала), и после этого мы бы иметь возможность использовать их в комнате?

David3103 07.06.2019 15:42

Я думаю, да, потому что вам нужно иметь сущность (таблицу), чтобы использовать комнату db

Sushant Somani 08.06.2019 02:01
0
3
1 422
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

What we need:

We need to be able to use attach the external databases to the internal Room-Database without changing the path they are stored in. After adding them, we want to be able to use the databases with the Room Entity, Dao and Database objects. Is there any possible way to achieve this?

Возможно, будет проще не подключаться, поскольку вы можете использовать отдельный экземпляр базы данных Room. Без него вам нужно было бы иметь отдельные DAO для обслуживания прикрепленного имени схемы (я полагаю). Говоря, что пример ниже (на основе чего-то, с чем я играл для кого-то, и, следовательно, довольно запутанных имен столбцов).

например предполагая ATTACH DATABASE .... AS 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.

  • Примечание, конечно, вышеизложенное предполагает, что схемы дополняют друг друга (не обязательно точны).

Не обязательно точно?

Также охватывает комментарий: -

You have to migrate data to room itself first.

  • Немного поиграв, можно обойти необходимость мигрировать, обманув комнату, установив для user_version значение 0. В этом случае Room устанавливает номер версии (ограниченное тестирование). Однако я не уверен, что GreenDao или ваш сервер сделают из этого (ваше домашнее задание).

  • Мое ограниченное тестирование было связано с распространенной проблемой при переносе столбца с INTEGER PRIMARY KEY, то есть без AUTOINCREMENT. Комната при переносе будет жаловаться на несоответствие схемы. Поэтому я намеренно не стал кодировать АВТОИНКРЕМЕНТ, установил для user_version значение 0 и никаких жалоб на доступ к базе данных через Room. Также используется тип столбца румпельстилскин и никаких жалоб.

Таким образом, я считаю, что вы можете обойти проблемы миграции с ужасным ожидаемым/найденным, установив для user_version значение 0 (и, следовательно, я считаю, что обойти миграцию). Очевидно, что имена столбцов должны совпадать, если они определены в Entity и не игнорируются.

  • Я также пытался добавить столбец, не определенный в Entity, и использовать приведенные выше результаты без жалоб (эти тесты должны быть очевидны в приведенном ниже коде).

Примеры)

Ниже приведен пример базы данных комнаты с двумя сущностями, и в целях тестирования базы данных разное, созданной за пределами комнаты, которая в достаточной степени соответствует базе данных комнаты, чтобы ее можно было использовать, т. е. имена столбцов сущностей совпадают.

Другая база данных

Другая база данных, не относящаяся к комнате, создается с помощью подкласса 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);
    }
}
  • см. комментарии для странностей / намеренно добавленных несоответствий
  • это заполняется, доступ к базе данных альтернативной комнаты и через прикрепленную БД в MainActivity.java ниже

База данных комнат

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

и ПлатныйНеоплачиваемый.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());
    }
}

Один интерфейс Дао.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();
}

Собираем все вместе

Наконец, деятельность, которая: -

  1. Создает и заполняет базу данных разное (не для помещений) (если она не существует) для тестирования. Установка user_version (версия базы данных в Android Talk) на 0.

  2. При необходимости создает версию базы данных Room.

  3. Добавляет пару строк в версию Room.
  4. Выводит данные версии Room в журнал.
  5. Создает альтернативную базу данных RoomDatabase, используя базу данных разное.
  6. Выводит данные в базу данных разное через Room.
  7. Присоединяет базу данных разное к версии Room.
  8. Выводит данные из обоих через исходную базу данных Roomdatabase, получая доступ к прикрепленной базе данных разное через дополнительные интерфейсы DAO, которые включают разное.????.

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

Это сработало. Большое спасибо за подробный ответ, я не знал, что это можно сделать так легко.

David3103 11.06.2019 17:09

О, чувак, как больно видеть такой отличный и длинный ответ всего с двумя плюсами.

rekire 19.02.2021 13:28

Другие вопросы по теме