【发布时间】:2018-09-05 14:02:25
【问题描述】:
我想问是否可以在一个项目下拥有多个数据库,并使用 Room Persistence Library?动态更改数据库的选择。 谢谢
【问题讨论】:
-
我知道在 Room 之外是可能的。 SQLiteOpenHepler 将数据库名称作为参数
-
@KostasC 请查看this 帖子
标签: android android-room
我想问是否可以在一个项目下拥有多个数据库,并使用 Room Persistence Library?动态更改数据库的选择。 谢谢
【问题讨论】:
标签: android android-room
这是可能的。 假设您有两组实体和两组 DAO。您可以通过以下方式访问两个数据库:
应用数据库 1:
@Database(entities = {/*... the first set of entities ...*/}, version = 1)
public abstract class AppDatabase1 extends RoomDatabase {
// the first set of DAOs
}
AppDatabase2:
@Database(entities = {/*... the second set of entities ...*/}, version = 1)
public abstract class AppDatabase2 extends RoomDatabase {
// the second set of DAOs
}
请注意,您将使用两个不同的文件名。
AppDatabase db1 = Room.databaseBuilder(getApplicationContext(), AppDatabase1.class, "database1.db").build();
AppDatabase db2 = Room.databaseBuilder(getApplicationContext(), AppDatabase2.class, "database2.db").build();
在这种情况下,您可以同时使用这两个数据库,但无法在它们之间创建查询。如果您需要附加两个数据库,那么您应该查看@Anees 提供的链接
【讨论】:
Each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances within a single process. 我们如何使用单个数据库实例创建多个数据库或表
如果数据库具有相同的架构,您可以重用实体和 DAO,并在它们之间动态切换(如果您想为每个用户使用不同的数据库文件,这很有帮助)。
实体类
@Entity
public class User {
@PrimaryKey
@NonNull
public String uid;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
}
DAO 类
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
数据库类
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
DatabaseClient 类
public class DatabaseClient {
private Context mCtx;
private AppDatabase appDatabase;
private static String databaseName;
private static DatabaseClient mInstance;
private DatabaseClient(Context mCtx, String dbName) {
this.mCtx = mCtx;
if(databaseName == null || !databaseName.equalsIgnoreCase(dbName)) {
databaseName = dbName;
}
appDatabase = Room.databaseBuilder(mCtx, AppDatabase.class, databaseName).build();
}
public String getDatabaseName() {
return databaseName;
}
public static synchronized DatabaseClient getInstance(Context mCtx, String dbName) {
if (mInstance == null || databaseName == null || !databaseName.equalsIgnoreCase(dbName)) {
mInstance = new DatabaseClient(mCtx, dbName);
}
return mInstance;
}
public AppDatabase getAppDatabase() {
return appDatabase;
}
}
现在您可以通过在参数中传递其名称来基于特定数据库进行查询,在我的例子中假设为myDb
List<User> users = DatabaseClient.getInstance(getApplicationContext(), myDb).getAppDatabase().userDao().getAll()
请记住,每当您使用数据库名称执行第一次调用时,它都会创建数据库文件。如果新用户到达并调用插入其信息,它会自动创建一个新的数据库文件并将信息数据插入其中。
【讨论】:
刚刚用koin解决了。我正在创建一个即时消息应用程序,并且需要多个帐户登录。 user1登录我的应用程序后,可以获得带有im_id的数据库名称,然后通过注入,我创建了带有id的数据库。然后 user1 一次注销,我只是卸载数据源模块并跳转到登录活动。 User2 然后登录,我重新加载数据源模块并使用他的 im_id 为 user2 创建数据库。代码如下:
val dataSourceModule = module{
single {
Room.databaseBuilder(androidApplication(), AppDataBase::class.java, get<GsSelectedAndImTokenPersistence>().gsImToken?.gsImId ?: "im_database" )
.build()
}
single { get<AppDataBase>().gsInfoDao() }
single { get<AppDataBase>().gsGameInfoDao() }
single { get<AppDataBase>().gameClientDao() }
single { SharedPreferencesDataSourceImpl(androidContext()) } binds (
arrayOf(
ImDeviceIdPersistence::class,
GsSelectedAndImTokenPersistence::class
))
}
fun unLoadDataSourceModule() {
unloadKoinModules(dataSourceModule)
}
fun reLoadDataSourceModule() {
loadKoinModules(dataSourceModule)
}
有趣的是,即使 get().gsImToken?.gsImId 为null,则不会使用koin injection创建名为“im_database”的默认数据库。
这是我从服务器获取 im_id 后通过注入创建数据库的地方
viewModel.gsImToken.observe(provideLifecycleOwner(), {
ELogger.d("database initial","init database===")
// Incase of the datasource module is not loaded by now
KoinInitializer.reLoadDataSourceModule()
val gs: AppDataBase by inject()
gs.gsGameInfoDao().run {
viewModel.initDao(this)
}
})
还有登出的地方:
class SettingViewModel(
...,
val db: AppDataBase
): ViewModel() {
...
fun onLogout(){
...
db.close()
KoinInitializer.unLoadDataSourceModule()
...
}
}
【讨论】: