Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default values for new properties #157

Open
werelord opened this issue Aug 23, 2017 · 29 comments
Open

Default values for new properties #157

werelord opened this issue Aug 23, 2017 · 29 comments
Labels
enhancement New feature or request

Comments

@werelord
Copy link

Im using ObjectBox in a pet android project written in Kotlin.. I have a data class encoded as such (example):

@Entity data class Foo(var name:String, var description: String) {
    constructor() : this("", "") // parameter-less constructor
    @Id var id: Long = 0L
    // other stuff
}

Database already existed on the device.. I added an additional property to the Foo class (not in the data constructor):

@Entity data class Foo(var name:String, var description: String) {
    constructor() : this("", "") // parameter-less constructor
    @Id var id: Long = 0L
    var bar: String = ""
    // other stuff
}

defaulted, non-null object bar.. however when pulling existing records from the stored box, that value bar appears to be null.. I would expect it to be a blank string (default for the parameter); is the violation of not-null guarantee coming from the Java interop?

I fixed the issue by nuking the database; afterwords the entity created and stored is not-null.. But this brings up any possible future updates that may violate this.

Is there a "database upgrade" method for updating already stored objects between versions, adding default values to new properties, or am I resigned to make anything added to an existing model be nullable (and handle it as such)?

@greenrobot
Copy link
Member

It's quite easy to trick the Kotlin non-nullable types, e.g. with JSON parsers. Same is true for ObjectBox. We will target those kind of oddities in a future version. For now, we suggest to either use nullable types, or have some update code in place. Recipe for the latter:

  1. Have a flag (via SharedPrefs for example) like "fooBarChecked"
  2. If flag is false, run a migration and set the flag
  3. Migration: run a query to get all Foos with bar == null and update the resulting list

@greenrobot
Copy link
Member

Maybe an annotation for defining "defaults" for null values? E.g.

@DefaultForNull("")
var bar: String = ""

@greenrobot-team
Copy link
Member

No response. Closing. -ut

@greenrobot-team
Copy link
Member

greenrobot-team commented Feb 26, 2019

Reopening to discuss default values (in Java, but also Kotlin classes with default values).

This also affects queries (merged from #589), e.g. if adding intProperty, then querying box with existing entities:

box.query().equal(intProperty, 0) // no results
box.query().isNull(intProperty) // has results

-ut

@greenrobot-team greenrobot-team changed the title Stored object "upgrade", defaulted property in Kotlin Default values for new properties Feb 26, 2019
@greenrobot-team greenrobot-team added the enhancement New feature or request label Feb 26, 2019
@greenrobot-team greenrobot-team added this to the 2.6.0 milestone Apr 28, 2020
@greenrobot-team
Copy link
Member

greenrobot-team commented May 4, 2020

With 2.6.0-RC there is very initial support for a @DefaultValue annotation. Currently only String properties are supported, and only the empty string "" as default value.

Usage:

@DefaultValue("")
String exampleProperty;
@DefaultValue("")
var exampleProperty: String = ""

Edit: This annotation currently simply adds the new built-in NullToEmptyStringConverter to the property:

public class NullToEmptyStringConverter implements PropertyConverter<String, String> {
@Override
public String convertToDatabaseValue(String entityProperty) {
return entityProperty;
}
@Override
public String convertToEntityProperty(@Nullable String databaseValue) {
if (databaseValue == null) {
return "";
}
return databaseValue;
}
}

So to customize default values, create your own converter and replace @DefaultValue with @Convert.

@greenrobot
Copy link
Member

greenrobot commented May 4, 2020

My first assessment is that we should keep default values separate from queries. E.g. if you want to, you can query for null values.

@greenrobot-team greenrobot-team modified the milestones: 2.6.0, 3.0 Jun 15, 2020
@pratikbutani
Copy link

pratikbutani commented Aug 5, 2020

What about the default values of booleans? I had taken a boolean variable. It's giving me the default value "false" but I want true then?

@greenrobot
Copy link
Member

@pratikbutani The default of booleans is false. Are you saying you experienced something else?

@pratikbutani
Copy link

@pratikbutani The default of booleans is false. Are you saying you experienced something else?

I want default true, How can I do that?

@RobbWatershed
Copy link

Reopening to discuss default values (in Java, but also Kotlin classes with default values).

This also affects queries (merged from #589), e.g. if adding intProperty, then querying box with existing entities:

box.query().equal(intProperty, 0) // no results
box.query().isNull(intProperty) // has results

-ut

Still present in v2.9.1 - Default values for new non-string (booleans, integers...) fields are created as null in the database.

As far as I know, our only workaround options are to :

  • Append or().isNull to all queries that use the new field
    or
  • Manually set new fields with the null value to their intended default value at app startup after the update

Having an annotation that allows to set these default values on the Entity would be a lot less cumbersome 😄

@greenrobot-team greenrobot-team modified the milestones: 3.0, Future release Oct 19, 2021
@mecoFarid

This comment was marked as duplicate.

@neroux
Copy link

neroux commented Apr 10, 2023

@greenrobot, would you have any update on this?

I just ran into this issue and am not quite sure either how to handle it. Here, it's not even just about primitive types, but about List<String>.

As I am initilalise the new list member with a default value, my assumption was ObjectBox was handling this transparently. First running the class'es default initialisation and then applying to that instance any data it has stored. That does not seem to be the case.

Would that actually be an idea? Respectively, how should this be handled right now?

Thanks.

@neroux
Copy link

neroux commented Apr 11, 2023

While @Convert may have been a hack, it may still have been the most elegant workaround.

[ObjectBox] @Convert dbType type is not supported, use a Java primitive wrapper class

Unfortunately that does not work either, as it expects a primitive wrapper, even though ObjectBox does support List.

I am sure there may be technical reasons for that, but I am afraid this limitation really is in contrast to ObjectBox's approach of handling database changes transparently.

Is there a reason why ObjectBox ignores the default value for new fields (which are not in the database) and specifically sets them to Java's non-initialised counterparts? If ObjectBox could keep the value of the default initialiser for such fields, the whole issue would be fixed.

Any comments much appreciated, @greenrobot-team, @greenrobot.

@greenrobot-team
Copy link
Member

greenrobot-team commented Apr 18, 2023

Is there a reason why ObjectBox ignores the default value for new fields (which are not in the database)

There is a value in the database for new properties: it's null (or 0/false), so the database returns that. The database does not differentiate between a property being null because it was just added or because it was explicitly set null. Hence the discussion above to add a @DefaultForNull annotation to return a different value if the database value is null.

Thanks for sharing your edge case with List<String> where using @Convert doesn't work! This might just be a bug with the annotation processor.

A workaround is to use a type supported with @Convert. E.g. String and then e.g. map a string list to a JSON construct.

@neroux

This comment was marked as duplicate.

@greenrobot-team
Copy link
Member

greenrobot-team commented Apr 18, 2023

Update: using List<String> for the @Convert dbType can never work due to how Java generics work (there is no such thing as List<String>.class in Java).

However, using String[].class should work. But doesn't because the generated cursor code is incorrect. Will create an internal issue for this.

@neroux

This comment was marked as duplicate.

@neroux

This comment was marked as spam.

@neroux

This comment was marked as duplicate.

@neroux

This comment was marked as off-topic.

@greenrobot

This comment was marked as off-topic.

@neroux

This comment was marked as duplicate.

@greenrobot

This comment was marked as off-topic.

@neroux

This comment was marked as off-topic.

@greenrobot

This comment was marked as off-topic.

@neroux

This comment was marked as off-topic.

@greenrobot

This comment was marked as off-topic.

@neroux

This comment was marked as off-topic.

@greenrobot
Copy link
Member

@greenrobot-team To pick up on the annotation, maybe this would be better?

@DefaultForNull(factory = MyValueProvider.class)

Where MyValueProvider would implement an interface "ValueProvider" which basically has a single method like Object provideValue(). The advantage would be that it could provide complex types like lists, arrays and flex. Disadvantage: another thing passed down to JNI, and not clear if wrappers like Integer demand extra effort for primitive types like int.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

7 participants