realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.45k stars 1.75k forks source link

Automatic handling of migration to EmbeddedObjects does not work #7769

Closed adnandautovic closed 1 year ago

adnandautovic commented 1 year ago

How frequently does the bug occur?

Always

Description

This feature was added to Realm Core in 12.6.0 and the version of Core currently used in Realm Java is 12.13.0, so I would excpect that manual clearing of orphaned objects is not necessary when migrating from referenced objects to embedded objects. Instead, the app crashes at startup with the message Cannot convert 'Child' to embedded: at least one object has no incoming links and would be deleted..

So, in the current version of the app there is

public class Parent extends RealmObject {

@PrimaryKey
private long id;

private Child child;

public Parent() {}

}

and

public class Child extends RealmObject {

private String name;

public Child() {}

}

So far, no care has been taken to delete orphaned children from the database when a parent is deleted, which means for most users there are going to be Child objects that are not referenced by a Parent. We want to change that now and turn Child into an EmbeddedObject, as follows:

// Unchanged
public class Parent extends RealmObject {

@PrimaryKey
private long id;

private Child child;

public Parent() {}

}

and

@RealmClass(embedded = true)
public class Child extends RealmObject {

private String name;

public Child() {}

}

With a new migration added:

class Migration : RealmMigration {
    override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
        val schema = realm.schema
        var oldVersionVal = oldVersion

        if (oldVersionVal == 1L) {
            schema.get("Child")?.isEmbedded = true
            oldVersionVal++
        }
}

And on app startup:

private function startup() {
        Realm.init(this)
        val config = RealmConfiguration.Builder()
                .name(packageName)
                .schemaVersion(2)
                .migration(Migration())
                .build()
        Realm.setDefaultConfiguration(config)
}

However, the automatic handling of the backlinks does not work (i.e. automatic deletion of Child objects in this case). The app crashes on startup. I know from our iOS app that this feature does work in Realm Swift, leading me to believe there is an issue with Realm Java and not with Realm Core.

Stacktrace & log output

java.lang.RuntimeException: Unable to create application my.app.name.App: java.lang.IllegalStateException: Cannot convert 'Child' to embedded: at least one object has no incoming links and would be deleted.
  at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6739)
  at android.app.ActivityThread.access$1500(ActivityThread.java:256)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2090)
  at android.os.Handler.dispatchMessage(Handler.java:106)
  at android.os.Looper.loopOnce(Looper.java:201)
  at android.os.Looper.loop(Looper.java:288)
  at android.app.ActivityThread.main(ActivityThread.java:7842)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
  Caused by: java.lang.IllegalStateException: Cannot convert 'Child' to embedded: at least one object has no incoming links and would be deleted.
  at io.realm.internal.Table.nativeSetEmbedded(Native Method)
  at io.realm.internal.Table.setEmbedded(Table.java:796)
  at io.realm.RealmObjectSchema.setEmbedded(RealmObjectSchema.java:595)
  at my.app.name.api.Migration.migrate(Migration.kt:109)
  at io.realm.BaseRealm$6.onMigrationNeeded(BaseRealm.java:892)
  at io.realm.internal.OsSharedRealm.runMigrationCallback(OsSharedRealm.java:576)
  at io.realm.internal.OsSharedRealm.nativeGetSharedRealm(Native Method)
  at io.realm.internal.OsSharedRealm.<init>(OsSharedRealm.java:174)
  at io.realm.internal.OsSharedRealm.getInstance(OsSharedRealm.java:259)
  at io.realm.BaseRealm.<init>(BaseRealm.java:142)
  at io.realm.BaseRealm.<init>(BaseRealm.java:109)
  at io.realm.Realm.<init>(Realm.java:161)
  at io.realm.Realm.createInstance(Realm.java:535)
  at io.realm.RealmCache.createInstance(RealmCache.java:508)
  at io.realm.RealmCache.doCreateRealmOrGetFromCache(RealmCache.java:461)
  at io.realm.RealmCache.createRealmOrGetFromCache(RealmCache.java:422)
  at io.realm.Realm.getDefaultInstance(Realm.java:443)
  at my.app.name.App.startup(App.kt:48)
  at my.app.name.App.onCreate(App.kt:32)
  at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1223)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6734)
  at android.app.ActivityThread.access$1500(ActivityThread.java:256) 
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2090) 
  at android.os.Handler.dispatchMessage(Handler.java:106) 
  at android.os.Looper.loopOnce(Looper.java:201) 
  at android.os.Looper.loop(Looper.java:288) 
  at android.app.ActivityThread.main(ActivityThread.java:7842) 
  at java.lang.reflect.Method.invoke(Native Method) 
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 

Can you reproduce the bug?

Always

Reproduction Steps

No response

Version

10.13.2-transformer-api

What Atlas App Services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

Android API 32

Build environment

Android Studio version: Android Studio Electric Eel | 2022.1.1 Patch 1 Build #AI-221.6008.13.2211.9514443 Android Build Tools version: 34-rc1 Gradle version: 7.5

edualonso commented 1 year ago

Hi @adnandautovic. I can reproduce your issue. It appears to be an oversight on our side after Core added this functionality. It looks like we need to extend setEmbedded to accept a handleBackLinks modifier.

adnandautovic commented 1 year ago

Thank you for the quick response!

clementetb commented 1 year ago

Closed as it has been fixed by #7772