Android Room 可以管理多个数据库并从模板数据库创建数据库吗?

Can Android Room manage multiple databases and create databases from a template database?

关于以下最佳实践或选项的任何想法、建议或想法?

问题: 使用 Android Room,在单个 Android 应用程序中创建和处理多个数据库的实用方法是什么?

我正在尝试做的事情: 我有一个 Android 应用程序旨在管理多个研究主题。这个想法是用户可以创建特定于主题或研究的数据库,并能够存储这些主题的来源、附件和注释。例如,一个用户可以有一个专门针对现代音乐史的数据库,一个关于狩猎史主题的第二个数据库,甚至可以有像微生物研究这样深入的主题。

我的想法是拥有单独的数据库而不是一个存储所有这些数据的数据库。特别是因为附件可以存储并很快占用 space。这些数据库可以在 phone / 平板电脑应用程序和桌面版本之间共享。有一个 Java 桌面版正在使用。

我做了什么我真的只在这里搜索并用谷歌搜索了一些但看起来有点模糊。我熟悉并已将更改迁移到数据库但不确定这是否始终是创建新数据库以及重命名等的最佳方式

此 Android 应用程序带有预定义和预填充的数据库作为演示。这个数据库已经 2 年没有变化了。因此,这个想法可能是有一个“template.db”,可用于创建新数据库并相应地重命名它们。

With Android Room, what is the practical way to approach creating and handling multiple databases in a single Android app?

你当然可以处理多个数据库和基于相同模式的多个数据库。

问题是如何确定有哪些数据库可以使用。如果所有数据库都位于同一路径(甚至多个路径),则可以使用它。另一种方法可能是拥有数据库的数据库。

这是一个利用数据库的数据库(“MasterDatabase”)并允许访问 x 个数据库的示例。

首先是 MasterDatabase,它有一个简单的 table,带有一个 id 列(可以省略)和一个数据库名称列。 table (@Entity) 根据 :-

被命名为 MasterDatabaseList
@Entity(
        indices = { @Index(value = "databaseName", unique = true)
        }
)
class MasterDatabaseList {
    @PrimaryKey
    Long id;
    String databaseName;

    public MasterDatabaseList() {}

    @Ignore
    public MasterDatabaseList(String databaseName) {
        this.databaseName = databaseName;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDatabaseName() {
        return databaseName;
    }

    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
}
  • 注意 databaseName 列上的唯一索引

伴随 table 的是 MasterDao 和 @Dao class :-

@Dao
abstract class MasterDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(MasterDatabaseList masterDatabaseList);
    @Query("SELECT * FROM masterdatabaselist")
    abstract List<MasterDatabaseList> getAllDatabases();
}
  • 允许插入或提取行。
  • 重复的数据库将被忽略,因此不会被添加。

MasterDatabase 是@Database class(它将之前的 classes 与数据库联系起来)并包含一个获取可以访问 MasterDao 的数据库:-

@Database(
        entities = {MasterDatabaseList.class},
        version = 1
)
abstract class MasterDatabase extends RoomDatabase {
    abstract MasterDao getMasterDao();

    static volatile MasterDatabase instance = null;

    public static MasterDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context,MasterDatabase.class,"master.db")
                    .allowMainThreadQueries()
                    .build();
        }
        return instance;
    }
}

现在是模板数据库,Base????? (用于演示的简单单个 table 数据库)。首先是 table BaseTable @Entity class:-

@Entity
class BaseTable {
    @PrimaryKey
    Long id;
    String mydata;

    public BaseTable(){}

    @Ignore
    public BaseTable(String myData) {
        this.mydata = myData;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getMydata() {
        return mydata;
    }

    public void setMydata(String mydata) {
        this.mydata = mydata;
    }
}
  • 一个非常简单的 table,带有一个 id 列和一个包含一些字符串数据的列。

伴随着@Dao class BaseDao :-

@Dao
abstract class BaseDao {
    @Insert
    abstract long insert(BaseTable baseTable);
    @Query("SELECT * FROM basetable")
    abstract List<BaseTable> getAllBaseTables();
    @Update
    abstract int update(BaseTable baseTable);
}
  • 具有非常基本的插入、提取和更新

和之前一样 @Database class BaseDatabase :-

@Database(
        entities = {BaseTable.class},
        version = 1
)
abstract class BaseDatabase extends RoomDatabase {
    abstract BaseDao getBaseDao();

    public static BaseDatabase getInstance(Context context, String databaseName) {
        BaseDatabase instance = null;
        if (databaseName != null) {
            return Room.databaseBuilder(context, BaseDatabase.class, databaseName)
                    .allowMainThreadQueries()
                    .build();
        }
        return instance;
    }
}
  • 注意数据库名称需要如何传递,这基本上是满足多个数据库的关键。

有了所有的演示 Activity。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "DBINFO";

    MasterDatabase masterDB;
    MasterDao masterDao;

    /* 3 Lists that need to be synchronised index wise */
    /* i.e. each index position should hold the respective name/databaseobject/dao
    /* List of the known databases (their names) */
    List<MasterDatabaseList> masterDatabaseListList = null;
    /* List of the BaseDatabase objects */
    ArrayList<BaseDatabase> baseDatabaseList = new ArrayList<>();
    /* List of the BaseDao's */
    ArrayList<BaseDao> baseDaoList = new ArrayList<>();

    /* The current database */
    int currentBaseIndex = -1; /* Index into the three Lists */
    BaseDatabase currentDB = null;
    BaseDao currentDao = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        masterDB = MasterDatabase.getInstance(this);
        masterDao = masterDB.getMasterDao();
        masterDatabaseListList = masterDao.getAllDatabases();
        // Add default db1  if it does not exist
        if (masterDatabaseListList.size() < 1) {
            addBaseDB("db1");
        }
        buildBaseLists();

        /* Add some data to db1 IF it exists (it should) */
        setCurrentIndexDBandDao("db1");
        if (currentBaseIndex > -1) {
            currentDao.insert(new BaseTable("Blah for db1"));
        }

        /* Add some data to db2 (it will not exist) */
        /* noting that the database will be created if it does not exist */
        setCurrentIndexDBandDao("db2");
        if (currentBaseIndex == -1) {
            addBaseDB("db2");
        }
        if (currentBaseIndex > -1) {
            currentDao.insert(new BaseTable("Blah for db2"));
        }

        /* Extract and Log Data for ALL the BaseDatabase databases i.e. db1 and db2 */
        for(MasterDatabaseList masterdb: masterDao.getAllDatabases()) {
            Log.d(TAG,"Database is " + masterdb.getDatabaseName());
            setCurrentIndexDBandDao(masterdb.databaseName);
            if (currentBaseIndex > -1) {
                for(BaseTable bt: currentDao.getAllBaseTables()) {
                    Log.d(TAG,"Extracted Base Table  row where MyData is" + bt.getMydata());
                }
            }
        }
    }

    /* Add a new Database */
    /* Note that it assumes that it will now be the current */
    /* so the current values are set */
    private void addBaseDB(String baseDBName) {
        masterDao.insert(new MasterDatabaseList(baseDBName));
        buildBaseLists();
        setCurrentIndexDBandDao(baseDBName);
    }

    /* Build/ReBuild the 3 Lists according to the master database */
    /* This could be better managed so as to not rebuild existing database/dao objects */
    private void buildBaseLists() {
        int ix = 0;
        baseDatabaseList.clear();
        baseDaoList.clear();
        masterDatabaseListList = masterDao.getAllDatabases();
        // Loop through the databases defined in the master database adding the database and dao to the respective lists
        for (MasterDatabaseList masterDB: masterDao.getAllDatabases()) {
            BaseDatabase baseDB = BaseDatabase.getInstance(this, masterDB.getDatabaseName());
            baseDatabaseList.add(baseDB);
            baseDaoList.add(baseDB.getBaseDao());
            ix++;
        }
    }

    /* Set the current trio according to the database name that is:*/
    /*  1.the currentBaseIndex for the 3 Lists */
    /*  2. the BaseDatabase object */
    /*  3. the BaseDao */
    /* The index value (currentBaseIndex) is also returned */

    private int setCurrentIndexDBandDao(String baseDBName) {
        currentBaseIndex = getListIndexByBaseDBName(baseDBName);

        if (currentBaseIndex > -1) {
            currentDB = baseDatabaseList.get(currentBaseIndex);
            currentDao = baseDaoList.get(currentBaseIndex);
        }
        return currentBaseIndex;
    }

    /* Get the index according to the database name passed */
    /* note -1 signifies not know/found */
    private int getListIndexByBaseDBName(String baseDBName) {
        masterDatabaseListList = masterDao.getAllDatabases(); // OverKill????
        int rv = -1; // default to not found
        for(int i=0; i < masterDatabaseListList.size();i++) {
            if (masterDatabaseListList.get(i).databaseName.equals(baseDBName)) {
                rv = i;
                break;
            }
        }
        return rv;
    }

    /* Output all rows from the BaseTable for data extracted by the BaseDaos getAllBaseTables */
    private void logBaseData(List<BaseTable> baseTableList) {
        Log.d(TAG,"Current Database Index is " + currentBaseIndex + " DB name is " + masterDatabaseListList.get(currentBaseIndex).getDatabaseName());
        for(BaseTable bt: baseTableList) {
            Log.d(TAG,"\tMyData value is " + bt.getMydata());
        }
    }
}

结果

当上面是运行第一次时日志包括:-

2021-09-16 11:39:30.262 D/DBINFO: Database is db1
2021-09-16 11:39:30.278 D/DBINFO: Extracted Base Table  row where MyData isBlah for db1
2021-09-16 11:39:30.278 D/DBINFO: Database is db2
2021-09-16 11:39:30.284 D/DBINFO: Extracted Base Table  row where MyData isBlah for db2

并通过 Android Studio 的 App Inspector 数据库 :-

对于 db2 BaseTable :-

  • 注意 以上仅旨在涵盖使用多个数据库的基础知识,旨在进行简单的解释,因此代码已保持简短。对于要分发的应用程序,它可能是 unacceptable。