Skip to content

Adding non-null Kotlin property causes crash when getting existing entity #589

Open

Description

@LeeKingHung

Edit: The original issue described below about default values has moved to #157.

ℹ This issue is now about correctly detecting not-nullable properties of Kotlin data classes.

Issue Basics

  • ObjectBox version (are using the latest version?): 2.2

I added a new int (not Integer) property into an existing entity. And I found that the new property values for existing record are all null instead of default value (0 in this case), and the the equal query will always return empty result.

box.query().equal( intProperty, 0) -> zero result
box.query().notEqual( intProperty, 0) -> zero result
box.query().isNull( intProperty ) -> non-empty result

Is this considered a bug? Or I should expect null for primitive property

Activity

  1. greenrobot-team commented on Oct 29, 2018

    @greenrobot-team
    Member

    Thanks for reporting!

    Internally, added properties are by default null. That should be correct.

    ping @greenrobot Though not sure if the database should treat null as if the default value was set (0, false, etc.) for primitive types?

    -ut

  2. jrcacd commented on Nov 8, 2018

    @jrcacd

    the same in our code.

    We added a field

    @Entity
    data class Notes(...
    var aaa : String = "", //(kotlin)
    ...)
    

    And the field is null (and it makes no sense)

  3. motornyimaksym commented on Feb 25, 2019

    @motornyimaksym

    Interesting that we got a crash in case when we add new field to entity with not nullable type. E.g.

    1. Create entity with one field.
    2. Run the app. Put new entity in db.
    3. Add new field to entity e.g. var someField: Int = 0.
    4. Run the app and get all saved entities from db.
      In our case it crashes. Is it means that automatic migration is totally broken when we use kotlin?
      @greenrobot-team @greenrobot Should I create standalone ticket for this case with an example of project where bug can be reproduced?
  4. changed the title [-]New property in existing entity[/-] [+]New property in existing entity (ObjectBox default values)[/+] on Feb 26, 2019
  5. greenrobot-team commented on Feb 26, 2019

    @greenrobot-team
    Member

    @Yazon2006 Just tried this with a very simple example and val someField: Int = 0 works, but obviously val anotherField: String = "default" does not.

    As a rule of thumb: when adding new properties make them nullable (so String? instead of String). Maybe someone can also come up with some constructor magic (probably only for regular classes).

    // before
    @Entity
    data class Example(
            @Id var id: Long = 0
    )
    // after
    @Entity
    data class Example(
            @Id var id: Long = 0,
            val someProperty: Int = 42, // primitive type, maps to 0 for existing entities
            val anotherProperty: String? = "default" // nullable for compat with previous model
    )

    The issue is that for properties stored as primitives (internally we call this scalars, like long, double, Date) in ObjectBox a null entry is mapped to a non-nullable type when getting an object, e.g. 0 for int. But if the property is stored as a nullable type, like String, ObjectBox will map no entry to null instead when getting the object.

  6. motornyimaksym commented on Feb 26, 2019

    @motornyimaksym

    Yes it's pretty reasonable, I understand. But it can be a bit painful and confusing. E.g. when Entity implements some interface with non null fields we still need to use non null values. And for such case I used workaround like this:

    interface SyncItem {
    	var itemId: String
    	var isSynchronized: Boolean
    }
    
    class Entity(
    		//ugly duplicated field
    		var dbItemId: String? = null,
    		override var isSynchronized: Boolean = false
    ) : SyncItem {
    
    	override var itemId: String
    		get() = dbItemId ?: generateItemId()
    		set(value) {
    			dbItemId = value
    		}
    }
    
    

    I would be glad to know if there are other more convenient ways to handle such kind of problems.

  7. greenrobot-team commented on Feb 26, 2019

    @greenrobot-team
    Member

    We could probably fix this by trying to detect non-null Kotlin properties. Then flag them as such so ObjectBox will not try to init them with null.

    The byte-code produced by Kotlin annotates non-null properties with org/jetbrains/annotations/NotNull. Based on a quick check the ObjectBox processor can see it.
    -ut

  8. motornyimaksym commented on Feb 26, 2019

    @motornyimaksym

    Sounds awesome! Thank you for your efforts! Looking forward.

  9. r4zzz4k commented on Feb 27, 2019

    @r4zzz4k

    There are a ton of other artifacts providing these kind of annotations. Here is the relevant ones from the list IntelliJ IDEA covers by default, with Maven coordinates for the artifacts containing them just for the reference:

    • org.jetbrains.annotations.NotNull (org.jetbrains:annotations:17.0.0)
    • javax.annotation.Nonnull (e.g. com.google.code.findbugs:jsr305:3.0.2)
    • edu.umd.cs.findbugs.annotations.NonNull (com.google.code.findbugs:findbugs-annotations:3.0.1)
    • android.support.annotation.NonNull (com.android.support:support-annotations:28.0.0 from https://maven.google.com)
    • androidx.annotation.NonNull (androidx.annotation:annotation:1.0.2 from https://maven.google.com)
    • org.checkerframework.checker.nullness.qual.NonNull (org.checkerframework:checker-qual:2.6.0)
    • com.android.annotations.NonNull (part of Android SDK)

    I don't say every one of them should be supported, but it might be reasonable to cover at least javax.annotation and maybe Android ones in addition to Kotlin / JetBrains ones, if Java people find this convenient.

  10. greenrobot-team commented on Mar 4, 2019

    @greenrobot-team
    Member

    OK, sorry, I completely let multiple issues get mangled together here.

    @LeeKingHung @jrcacd and others, refer to #157 for the issue that default values are not as expected.

    ℹ This issue is now about correctly detecting not-nullable properties of Kotlin data classes.

    @r4zzz4k Thanks for adding this. For now with Kotlin it is more pressing as it will actually lead to a crash due to Kotlin's runtime nullability checks.

  11. changed the title [-]New property in existing entity (ObjectBox default values)[/-] [+]Adding non-null Kotlin property causes crash when getting existing entity[/+] on Mar 4, 2019
  12. chaxiu commented on Oct 14, 2019

    @chaxiu

    @greenrobot-team
    Hi, greenrobot team, any update for this issue?

  13. greenrobot-team commented on Oct 15, 2019

    @greenrobot-team
    Member

    @chaxiu No. But it's easy to avoid by making sure to use nullable properties. See my comment: #589 (comment)

  14. FeamCoco commented on Jan 15, 2020

    @FeamCoco

    I used an annotation that seemed to solve the problem, at least I did.
    The object to add a field is called Apple, use @BaseEntity to declare an abstract class Fruit, move the new field in Apple to Fruit, and make Apple extend Fruit to solve this problem. I hope to help you.

  15. greenrobot-team commented on Apr 27, 2020

    @greenrobot-team
    Member

    An alternative workaround to keep a non-null Kotlin property: use @Convert. An example using String:

    @Convert(dbType = String::class, converter = NullToEmptyStringConverter::class)
    var nonNull: String = ""
    
    class NullToEmptyStringConverter : PropertyConverter<String, String?> {
        override fun convertToDatabaseValue(entityProperty: String): String? {
            return entityProperty
        }
    
        override fun convertToEntityProperty(databaseValue: String?): String {
            return databaseValue ?: ""
        }
    }
  16. added this to the 2.6.0 milestone on Apr 28, 2020
  17. greenrobot commented on Apr 28, 2020

    @greenrobot
    Member

    We just published 2.6.0-RC which should not crash.

    Additionally, you can now use @DefaultValue("") to introduce a new non-optional string property.

    We'd love some feedback on this before releasing! E.g. @DefaultValue may suggest "too much", another name might be @AbsentValue.

  18. greenrobot-team commented on May 4, 2020

    @greenrobot-team
    Member

    Actually adding a non-null property still crashes, but with 2.6.0-RC you will now see a Java exception with the actual reason.

    Also, with 2.6.0-RC, instead of manually adding a converter as suggested above, you can now just annotate a non-null String property with @DefaultValue(""):

    @DefaultValue("")
    var nonNull: String = ""

    Keeping this open to as mentioned add support for adding non-null Kotlin properties without user intervention.

  19. modified the milestones: 2.6.0, 3.0 on Jun 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions