【问题标题】:Android - Multiple database one projectAndroid - 多数据库一项目
【发布时间】:2018-09-05 14:02:25
【问题描述】:

我想问是否可以在一个项目下拥有多个数据库,并使用 Room Persistence Library?动态更改数据库的选择。 谢谢

【问题讨论】:

  • 我知道在 Room 之外是可能的。 SQLiteOpenHepler 将数据库名称作为参数
  • @KostasC 请查看this 帖子

标签: android android-room


【解决方案1】:

这是可能的。 假设您有两组实体和两组 DAO。您可以通过以下方式访问两个数据库:

  • 创建两个扩展 RoomDatabase 的类:

应用数据库 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 提供的链接

【讨论】:

  • 根据 Android 文档Each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances within a single process. 我们如何使用单个数据库实例创建多个数据库或表
【解决方案2】:

如果数据库具有相同的架构,您可以重用实体和 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()

请记住,每当您使用数据库名称执行第一次调用时,它都会创建数据库文件。如果新用户到达并调用插入其信息,它会自动创建一个新的数据库文件并将信息数据插入其中。

【讨论】:

  • 感谢您的建议。假设 User1 登录到应用程序,我已经创建了 User1DB; User1 然后从应用程序中注销。 User2 登录到应用程序,我已经创建了 User2DB。现在如果 User2 从应用程序中注销并且 User1 重新登录到应用程序,那么我如何访问 User1DB 那里?
  • @SolaimanHossain Room 使用构建器参数自动创建数据库,如果它不存在,则第一次创建。在此之后,如果您在使用相同 dbName 创建的 DatabaseClient 实例上查询任何内容,它将使用现有数据库。
  • @Deˣ 您能帮我编写上述解决方案的测试用例吗?我也面临同样的问题。
【解决方案3】:

刚刚用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()
     ...
    }
}

【讨论】:

  • 用动态数据库名动态创建数据库;确保在我们的项目中只打开一个数据库;随时重新打开数据库。 LateInit 数据库,当您从本地或远程获取数据库名称时创建。
猜你喜欢
  • 1970-01-01
  • 2021-10-30
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
  • 2019-05-24
  • 2021-11-17
  • 1970-01-01
  • 2012-09-14
相关资源
最近更新 更多