objectbox-java
objectbox-java copied to clipboard
Default values for new properties
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)?
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:
- Have a flag (via SharedPrefs for example) like "fooBarChecked"
- If flag is false, run a migration and set the flag
- Migration: run a query to get all Foos with bar == null and update the resulting list
Maybe an annotation for defining "defaults" for null values? E.g.
@DefaultForNull("")
var bar: String = ""
No response. Closing. -ut
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
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:
https://github.com/objectbox/objectbox-java/blob/220fc35ef296b9957a29207596abf8c1313dfcb5/objectbox-java/src/main/java/io/objectbox/converter/NullToEmptyStringConverter.java#L8-L22
So to customize default values, create your own converter and replace @DefaultValue
with @Convert
.
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.
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?
@pratikbutani The default of booleans is false. Are you saying you experienced something else?
@pratikbutani The default of booleans is false. Are you saying you experienced something else?
I want default true, How can I do that?
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 😄
Adding this to Future release
milestone, basically, means this will never happen. This should at least be supported in primitives. I have added a primitive field (long
) and ObjectBox browser and queries still see it as null
@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.
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.
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.
Thanks for the response, Uwe. Please let me address this one-by-one
As ObjectBox sees it there is a value in the database: it's null (or 0/false), so it returns that
But that's the point :) - it is not null. The object in the database does not have that value at all and the default initialiser is actually an instantiated object.
What is happening here is that ObjectBox does not find the value in question in the database (because it is new), discards the default initialised value and instead assumes it to be null (ignoring the discarded value).
Hence the discussion above to add a @DefaultForNull annotation to return a different value if the database value is null
We actually shouldn't even need an annotation. "Simply" don't discard the default value.
This might just be a bug with the annotation processor
I am not sure this is a bug, @Convert
seems to be explicitly for primitive values, which List
of course is not.
A workaround is to use a type supported with
@Convert
I am afraid I am not sure this is a viable workaround. This would completely distort the actual data structure and force the logic to work with non-native data.
IMHO, the most elegant approach would be to simply keep initialised values.
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.
@greenrobot-team, I am not sure why you marked my comment as duplicate, as it wasn't.
Could you please address the question at hand? I am not sure why it is not possible to keep the default initialiser. This would fix the whole issue.
Sorry Uwe, but it's not clear why you are hiding perfectlyfine responses as "duplicates" and do not address the question.
It would be highly appreciated if the original question could be address.ed Thanks.
@greenrobot
And to address your response, which you have edited in the meantime
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
How can there be a value for something which does not even exist? That may be an internal design issue with ObjectBox and I understand that newly introduced fields may be considered "null" by ObjectBox, but that's precisely the "bug" I was referring to. Such fields should not be returned as "null" but should preserve any values provided by the class'es default initialiser.
If that was the case, this whole issue would be immediately fixed. Is there a reason why this cannot be implemented?
I can only re-iterate what I wrote earlier
What is happening here is that ObjectBox does not find the value in question in the database (because it is new), discards the default initialised value and instead assumes it to be null (ignoring the discarded value)
And no, this is not a "duplicate" but emphasising the issue ;)
@greenrobot, @greenrobot-team, any statement on this? It has been two week since I first mentioned the issue and there was no proper response addressing it so far, so some tangible response would be appreciated.
I do understand that ObjectBox considers new fields as null, regardless of whether they exist or not, but that's the precise issue I was referring to - as were @werelord @pratikbutani @RobbWatershed @mecoFarid @ylk2534246654 as far as I understand.
Considering that this issue has been open and unaddressed for six years it would rather seem as if you are not going to provide a fix for it (= keeping the initialised values), but maybe you could at least elaborate on why that is not possible, as it is not clear why that should not be possible.
Such fields should not be returned as "null" but should preserve any values provided by the class'es default initialiser.
This is not how it works or should work. If e.g. a property is null it must be completely irrelevant what a constructor of some language does. Think about it on the database level.
Currently there's no convincing proposal. On top, interest in this issue is not that high and people seem to be able to workaround this by accepting null. Do you know how to do that?
This is not how it works or should work. If e.g. a property is null it must be completely irrelevant what a constructor of some language does
I appreciate the response, but I am sorry we are going in circles :) The property is not null. It is actually initialised. That initialised value is then overwritten by ObjectBox when it loads it from the database but does not find it (because it is new).
Currently there's no convincing proposal
I'd disagree, mentioned approach of keeping the initialised value should be a viable fix and, apart from what you just brought up and what I addressed, nobody has mentioned yet why that should not work.
The property is null in the database. Please refrain from further comments. Thank you.
Sorry, but I will not "refrain" because I do believe this issue should be addressed and you appear to refuse to provide the bare minimum of information and ignore it, respectively apparently try to shut up people.
It's fairly straightforward and a proper discussion would be appreciated and not this kind of attitude.
And no, the property is not null in the database, it does not exist there and for that very reason the default value should be kept. But I already wrote that very sentence more than once, not sure why you are ignoring that.
Again, you do not understand what is going on. Sorry, but you seem ignorant to what we have written. I do not consider this a fruitful conversation and we want to spend our time in a more productive way. If you continue with this attitude, you will be blocked.
the property is not null in the database, it does not exist there
Wrong. It is null in the DB because it does not exist there. What makes you think you know what's going on in the DB better than us?
Sorry, but the fact that you wrote this once again shows that you did not care to read or understand my earlier responses, as I addressed this very fact already more than once. It's bit pointless if the other party does not even try to understand what one is saying and I am not going to write the same response for the Nth time, only for it not to be read.
Hey buddy, I'm feeling the same way. Hugs and kisses.
I am not your buddy. I was looking for a professional discussion on this topic an how this issue could be fixed but only got
- ignored for two weeks
- told to shut up
- told that I'd be blocked if I did not stop an unspecified "attitude"
- and condescending behaviour from your side
Thanks, but no thanks. ObjectBox may want to re-evaluate its community mindset and how to interact with the userbase.
If I had been rude or impolite, I'd understand all of that, but none of that was the case and I have been even waiting for two weeks before I carefully pinged you.
@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
.