I'm working with the default realm instance, which contains some schemas. Now, I want to create a new schema in a different realm instance. So I have created a new Realm configuration, and when I query or store entities for this new schema, I use the new configuration, instead of the default one. In this way, I expect to avoid the neeed of create a migration for the new schema, due to I use it with a different database. But when I use the default realm instance, an exception is thrown (RealmMigrationNeededException), which tells me that I have to create a migration for my new schema. Can I avoid that in any way if I want to work with my new schema only in a new database?
Check Realm's document:
https://realm.io/docs/java/latest/#schemas
You need to define your 2 custom modules for different configurations.
For example, configB only cares about Cat.class schema.
// Create my module A
#RealmModule(classes = { Person.class, Dog.class })
public class MyModule {
}
// Create the module B
#RealmModule(classes = { Cat.class })
public class MyOtherModule {
}
// Set the module in the RealmConfiguration to allow only classes defined by the module.
RealmConfiguration configA = new RealmConfiguration.Builder()
.modules(new MyModule())
.name("A.realm")
.build();
RealmConfiguration configB = new RealmConfiguration.Builder()
.modules(new MyOtherModule())
.name("B.realm")
.build();
Related
Fairly new to room and having a hard time finding info on this. Currently we define our database like this:
#Database(entities = {TwcLocation.class,
CurrentObservation.class,
Day.class,
Hour.class,
Station.class,
StationCurrentObservation.class}, version = 1, exportSchema = false)
public abstract class TwcLocationDatabase extends RoomDatabase {
Now, we want to remove a bunch of unused entities so it looks like this:
#Database(entities = {TwcLocation.class, Tag.class}, version = 1)
#TypeConverters({TwcLocationTypeConverter.class})
public abstract class NbcRoomDatabase extends RoomDatabase {
Question: How do I do this migration?
You need to increment the version (so it will become version = 2).
Next, when building the Room instance by using the Room.databaseBuilder method, add the addMigration() line.
As migration, pass it the following to remove the tables:
// Migration from version 1 to 2
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
// Remove the table
database.execSQL("DROP TABLE day"); // This line for each table that you want to remove
}
};
Later on you might need multiple different migrations, you can do so by using the addMigrations() method:
Room.databaseBuilder(..., ..., ...)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.build()
Other option is to use fallbackToDestructiveMigration and increment database version. In such case you will not have to provide migrations. However it will clear data in all old tables.
I used Realm in conjunction with RxJava it this way:
public Flowable<List<EventEntity>> getAll() {
try (final Realm realm = Realm.getInstance(mRealmConfiguration)) {
RealmQuery<RealmEvent> query = realm.where(RealmEvent.class);
Flowable<RealmResults<RealmEvent>> result;
if (realm.isAutoRefresh()) {
result = query
.findAllAsync()
.asFlowable()
.filter(RealmResults::isLoaded);
} else {
result = Flowable.just(query.findAll());
}
return result
.unsubscribeOn(AndroidSchedulers.mainThread());
}
}
I use this chain on multiple places in app. For example:
return Observable.merge(
mEventRepository.getAll()
.toObservable(),
subjectNotificationChange
.flatMapMaybe(notification ->
mEventRepository.getAll()
.firstElement()
)
)
Problem is that I obtain exception: java.lang.IllegalStateException: This Realm instance has already been closed, making it unusable.
I looked at implementation method from of RealmObservableFactory and each call of subscribe method should create new instance of Realm. Entire situation looks as problem with references counting.
Do you know where is problem?
Java's try-with-resource closes the resource as soon as you leave the code block, but RxJava being lazy and all, only begins working when you actually subscribe, which happens after your code exits the getAll() function.
Edit: since you build a special Realm instance each time, passing configuration to it, the instance is not shared and therefore definitively closed each time.
Instead, initialize your Realm earlier using Realm.setDefaultConfiguration(config). Then, use Realm.getDefaultInstance() in your function so you access the default shared instance instead of creating a new one each time.
Edit2: the easiest solution is to keep a reference to the Realm instance:
class MyRepository {
private final Realm realm;
public MyRepository(Realm realm) {
this.realm = realm;
}
public Flowable<List<EventEntity>> getAll() {
RealmQuery<RealmEvent> query = realm.where(RealmEvent.class);
// ...
}
}
Realm realm = Realm.getDefaultInstance();
MyRepository repository = MyRepository(realm);
repository.getAll()
// ...
I find solution. It is bug in official example. When you call mentioned chain than must exist other open Realm instance for same thread. In other cases RealmResult is invalidated. Can be used solution mentioned by ESala.
I need some help with Realm.io, I've just been presented to it in a project I'm joining. The former developer showed the source code of our app recently and told me he is using Realm.io just to check if it's the first time the app has been opened by the user. Here is a code snippet of what he is using to do that on the onCreate() method. I'm using Android Studio for development.
Realm.init(getApplicationContext());
final Realm realm = Realm.getDefaultInstance();
final RealmResults<Configuracao> configuracoes =
realm.where(Configuracao.class)
.equalTo("chave", "primeiroAcesso")
.findAll();
The problem is that now I need to insert new data on the database so I've created a class that looks like this:
public class medicine extends RealmObject {
#PrimaryKey
private int id;
private String med;
private String doctor;
/* Setters and getters here */
}
I'm running into the RealmMigrationNeeded exception, I read the docs and I'm aware that I need to do the migration.
My question is: Where exactly do I put the migration code? Should I put it in the new class file?
Also, in the documentation they tell me that I need to change the version of the schema through something like this:
RealmConfiguration config1 = new RealmConfiguration.Builder()
.name("default1.realm")
.schemaVersion(3)
.migration(new Migration())
.build();
But they also say that if that version doesn't exist an exception will be thrown so I'm guessing I need to change the schema before doing that?
Do I have to change anything in the database itself and then call the migration inside the app, or the migration is the process to change the schema? Sorry about the long text but I'm really confused. Thanks for the help in advance.
Yes, you need to create a custom Migration class that must implement RealmMigration. The schema version you provide in the configuration is the version your Realm will have after the migration is run. You can see an example here: https://github.com/realm/realm-java/blob/master/examples/migrationExample/src/main/java/io/realm/examples/realmmigrationexample/model/Migration.java
In your case it would look something like this:
public class MyMigration implements RealmMigration {
#Override
public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
if (oldVersion == 2) {
schema.create("medicine")
.addField("id", int.class, FieldAttribute.PRIMARY_KEY)
.addField("med", String.class)
.addField("doctor", String.class);
}
}
}
I'm new to Realm. I'm using realm as a local db, and if the app is updated i don't want to lose data. What i did earlier is
public static Realm getRealmInstanse(){
RealmConfiguration config = new RealmConfiguration
.Builder()
.deleteRealmIfMigrationNeeded()
.build();
try {
return Realm.getInstance(config);
} catch (RealmMigrationNeededException e){
try {
Realm.deleteRealm(config);
//Realm file has been deleted.
return Realm.getInstance(config);
} catch (Exception ex){
throw ex;
//No Realm file to remove.
}
}
}
Now i think i should do the following:
public static Realm getRealmInstanse(){
RealmConfiguration config = new RealmConfiguration
.Builder()
.migration(new RealmMigration() {
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
}
})
.build();
return Realm.getInstance(config);
}
What should i do inside migrate() method in order to copy the data? And what about schema, should i uses schema version and for what purposes?
And what is the logic of changing the schema? For example, if for some reason i will change the structure of the db, can i just change the schema inside migrate() method?
I've found this example but i don't know actually if it is saving data or just changing the schema
if (oldVersion == 0) {
RealmObjectSchema personSchema = schema.get("Person");
// Combine 'firstName' and 'lastName' in a new field called 'fullName'
personSchema
.addField("fullName", String.class, FieldAttribute.REQUIRED)
.transform(new RealmObjectSchema.Function() {
#Override
public void apply(DynamicRealmObject obj) {
obj.set("fullName", obj.getString("firstName") + " " + obj.getString("lastName"));
}
})
.removeField("firstName")
.removeField("lastName");
oldVersion++;
}
What should i do inside migrate() method in order to copy the data?
Nothing, data is automatically kept between app updates (provided you have not changed the schema while also doing deleteRealmIfMigrationNeeded()).
If you change the database schema and have set deleteRealmIfMigrationNeeded(), the data will be deleted in order to migrate to the new schema automatically.
If you change the database schema and have not set deleteRealmIfMigrationNeeded(), you must provide a RealmMigration, or the app will crash with a "migration needed" exception.
For example, if for some reason i will change the structure of the db, can i just change the schema inside migrate() method?
Yes. You can interact with the DynamicRealm that is passed to #Override public void migrate() to specify the changes required to migrate to a new schema version.
You should give Realm's migration documentation a read.
Sidenote: building the RealmConfiguration as you are doing in your code should not be done every time you request an instance. Rather, do it once, preferably in your Application class. Also see configuring a realm.
I used Realm in my android untill now with
new RealmConfiguration.Builder(this) .build();
I just read later about the possibility to add schema and migration.
So in my new version for my app i want to add the migration feature.
so i changed the line above to:
new RealmConfiguration.Builder(this) .schemaVersion(0) .migration(new Migration()) .build();
but now I get the error
IllegalArgumentException: Configurations cannot be different if used to open the same file.
How can i change the configuration without deleting the database
I think your problem is that you are creating your RealmConfiguration multiple times. That shouldn't be a problem by itself (although it is inefficient), but the problem arises with your Migration class. Internally we compare all state in the configuration objects and if you didn't override equals and hashCode in your Migration class you have a case where new Migration().equals(new Migration()) == false which will give you the error you are seeing.
One solution is adding this:
public class Migration implements RealmMigration {
// Migration logic...
#Override
public int hashCode() {
return 37;
}
#Override
public boolean equals(Object o) {
return (o instanceof Migration);
}
}
When you set a new schema version with schemaVersion(), the version number should equal to or higher than the schema version of the existing realm file. The RealmMigration() you provide should then be able to convert older version of schemas to the new version.
I'd suggest to check your existing schema version first, then check your RealmObjects for appropreate conversion.