I would like to ask if is possible to have multiple database under one project, with Room Persistence Library? Changing dynamic the selection of the database. Thanks
I would like to ask if is possible to have multiple database under one project, with Room Persistence Library? Changing dynamic the selection of the database. Thanks
It is possible. Let's assume you have two sets of entities and two sets of DAOs. You can obtain access to two databases by:
AppDatabase 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
}
Note that you'll use two different file names.
AppDatabase db1 = Room.databaseBuilder(getApplicationContext(), AppDatabase1.class, "database1.db").build();
AppDatabase db2 = Room.databaseBuilder(getApplicationContext(), AppDatabase2.class, "database2.db").build();
In this case, you can use both databases, but you won't be able to create queries between them. If you need to attach the two databases, then you should take a look at the link @Anees provided
You can reuse the entities and DAO if databases have the same schema and dynamically switch between them (helpful if you want to have a different database file for each user).
Entity class
@Entity
public class User {
@PrimaryKey
@NonNull
public String uid;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
}
DAO class
@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 class
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
DatabaseClient class
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;
}
}
Now you can query based on a particular database by passing its name in the parameter in my case here let's say myDb
List<User> users = DatabaseClient.getInstance(getApplicationContext(), myDb).getAppDatabase().userDao().getAll()
Remember whenever you perform the first call with a database name, it creates the database file. If a new user arrives and calls to insert its info, it automatically creates a new database file and inserts the info data into it.
Just solved it with koin. I was creating an instant message app, and mutiple account login was required. After user1 logon my app, the databse name with im_id could be got, then by inject, I created the database with the id. Then user1 one logout , I just unload the datasource module and jump to the login activity. User2 then login, I reload the datasource module and created database for user2 with his im_id. Code as follows:
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)
}
The interesting is, even if get().gsImToken?.gsImId is null, it won't create the default database with name "im_database" by using koin inject.
And This is where I create the database by inject ,after I got the im_id from server
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)
}
})
And the logout place:
class SettingViewModel(
...,
val db: AppDataBase
): ViewModel() {
...
fun onLogout(){
...
db.close()
KoinInitializer.unLoadDataSourceModule()
...
}
}