How to migrate data from realm when updating application - android

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.

Related

What is the correct way to upgrade Realm database library version on Android?

We currently use Realm 6.x.
However when trying to update the library to the latest version (10.x), the app crashes during opening opening.
In the app, realm is currently configured to delete all data on schema change and compact db on open. But even with this setting the app crashes.
Is this normal that major version update causes crash?
What’s the proper way to upgrade? Is it possible to do it without loosing data?
Alright for realm database follow this approach.
RealmConfiguration config = new RealmConfiguration.Builder(context)
.schemaVersion(2) // Must be bumped when the schema changes
.migration(new MyMigration()) // Migration to run
.build();
Realm.setDefaultConfiguration(config);
// This will automatically trigger the migration if needed
Realm realm = Realm.getDefaultInstance();
public class MyMigration implements RealmMigration {
#Override
public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
// Migrate from version 0 to version 1
if (oldVersion == 0) {
RealmObjectSchema userSchema = schema.get("User");
userSchema.addField("testRealm", String.class);
oldVersion++;
}
if (oldVersion == 1) { // ...
// ...
}
}
#Override
public int hashCode() { return MyMigration.class.hashCode(); }
#Override
public boolean equals(Object object) { return object != null && object instanceof MyMigration; }
}
Also consider that
Realm.init(this) sets a default RealmConfiguration and should ONLY be called inside Application.onCreate

In Realm on android im getting "Field already exists" error but its on a fresh install?

ok. so i delete my app completely from android. Then on a fresh install i get the error
Field already exists in 'PortfolioCoin': color.
Why is realm trying to migrate on a fresh install?
I got this in my application file
Realm.init(this);
RealmConfiguration configuration = new RealmConfiguration.Builder()
.name(Realm.DEFAULT_REALM_NAME)
.schemaVersion(1)
.migration(new Migration())
//.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(configuration);
Realm.compactRealm(configuration);
and this is my migration file
public class Migration implements RealmMigration {
#Override
public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
// During a migration, a DynamicRealm is exposed. A DynamicRealm is an untyped variant of a normal Realm, but
// with the same object creation and query capabilities.
// A DynamicRealm uses Strings instead of Class references because the Classes might not even exist or have been
// renamed.
// Access the Realm schema in order to create, modify or delete classes and their fields.
RealmSchema schema = realm.getSchema();
if (oldVersion == 0) {
RealmObjectSchema portfolioCoinSchema = schema.get("PortfolioCoin");
portfolioCoinSchema
.addField("color", int.class)
.addField("totalValueBTC", double.class);
oldVersion++;
}
}
}
It happens because you're doing a fresh install, which already have the fields "color" and "totalValueBTC", and then you're trying to do a migration from 'oldVersion == 0', which is the default value.
So you're trying to add fields that already exist.
You should either check for a different version code, or you should use the "hasField(field)" method to check if it's already there, before trying to add it via a migration.

How can I change my code to better suit multiple tables using Realm?

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);
}
}
}

Open realm with new realmconfiguration

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.

How to properly handle Realm instance deletion

I have a situation where I want to handle a realm migration in a lazy fashion. Instead of using the Realm Migration api, which the realm developer's state is cumbersome, I want to delete the realm instance and then re-instantiate it only if I need to do a migration. How would I properly handle a this situation such that I only delete and re-instantiate the database ONLY IF it needs to be upgraded?
Say I have a model
#RealmClass
public class testmodel extends RealmObject {
private String foo;
private int bar;
public int getBar() {
return bar;
}
public void setBar(int bar) {
this.bar = bar;
}
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
and I want to add another string bazz into the model with the proper getters and setters.
Currently, I can do this as long as the first time I instantiate realm, I delete it. Like so:
Realm.deleteRealmFile(getApplicationContext());
realm = Realm.getInstance(getApplicationContext());
Now, let's suppose I didn't want to go blowing away my data every time the app starts, but still wanted to do this in the instance when I needed to. I thought a good way would be like this.
try{
Log.d(TAG, "started realm creation");
realm = Realm.getInstance(getApplicationContext());
Log.d(TAG, "successfully created realm");
}
catch (RealmException e){ // I tried RealmMigrationNeededException and IllegalState Exception but neither are caught
Log.d(TAG, "try deleting realm");
Realm.deleteRealmFile(getApplicationContext());
realm = Realm.getInstance(getApplicationContext());
Log.d(TAG, "deleted realm and remade successfully");
}
However, when I try this instead of the exception being caught, my app crashes.
How would I properly handle a this situation such that I only delete and re-instantiate the database ONLY IF it needs to be upgraded?
In Realm 0.81.0 you can use the new RealmConfiguration object:
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context)
.schemaVersion(42)
.deleteRealmIfMigrationNeeded()
.build();
This will do exactly what you are looking for.
Edit:
JavaDoc is here: https://realm.io/docs/java/latest/api/io/realm/RealmConfiguration.Builder.html
And you can read more about setup here:
https://realm.io/docs/java/latest/#configuring-a-realm

Categories

Resources