I'm getting some JSON from a REST call to a remote server and when the object is being created from the JSON object it crashes.
I have a just two entities for now, with a 1 to 1 relation. Each User entity has one UserSchoolDetails in it and all the information is passed in the JSON.
When I try to save the entry to the box it crashes.
public static void updateUser() {
//get the Box
Box<User> userBox = SmartAlumni.boxStore.boxFor(User.class);
//store the logged in User
userBox.put(SmartAlumni.currentUser);
}
long __assignedId = collect313311(cursor, entity.id, PUT_FLAG_COMPLETE,
__id10, wallet, 0, null,
0, null, 0, null,
__ID_userSchoolDetailsId, entity.userSchoolDetails.getTargetId(), __id8, __id8 != 0 ? dateOfBirth.getTime() : 0,
__ID_is_admin, entity.getIs_admin() ? 1 : 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0);
FATAL EXCEPTION: main
Process: com.pacent.smartalumni, PID: 21845
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at io.objectbox.relation.ToOne.getTargetIdField(ToOne.java:248)
at io.objectbox.relation.ToOne.getTargetId(ToOne.java:236)
at com.pacent.smartalumni.data.models.UserCursor.put(UserCursor.java:100)
at com.pacent.smartalumni.data.models.UserCursor.put(UserCursor.java:16)
at io.objectbox.Box.put(Box.java:380)
at com.pacent.smartalumni.utils.Utilities.updateUser(Utilities.java:56)
at com.pacent.smartalumni.presentation.signup.SignInActivity.onDataLoaded(SignInActivity.java:132)
at com.pacent.smartalumni.presentation.signup.SignInActivity.onDataLoaded(SignInActivity.java:34)
at com.pacent.smartalumni.data.remote.SignUpDataSource$1.onResponse(SignUpDataSource.java:37)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
this is the JSON I get from the REST call
{"date_of_birth":"Aug 25, 2017 5:39:15 PM","email":"[email protected]","first_name":"damilola","id":0,"is_admin":false,"last_name":"ola","name":"dami","phone_number":"1111111111","_id":"59a052b381ee2c1ae2effdd9","school_details":{"checkIdOfTargetForPut":false,"debugRelations":false,"resolvedTargetId":0,"targetId":0,"virtualProperty":false},"wallet":"59a6d36bda891ea63bb35d49"}
@Entity
public class User implements IUser {
@Id
public long id;
@SerializedName("school_details")
public ToOne<UserSchoolDetails> userSchoolDetails;
}
@Entity
public class UserSchoolDetails {
@Id
public long id;
}
Replace @Id with @Id(assignable = true).
For which model?
The UserSchoolDetails model?
The one where you are assigning an own id to. In the json of the school details it looks like you need to add the target id to resolve the relation.
Hmm... Actually the JSON shouldn't be like that...
it should be
{
"_id": "59a052b381ee2c1ae2effdd9",
"phone_number": "1111111111",
"__v": 0,
"security_token": "$2a$05$Ri1RB3xutZa8QQ2gq4fNMekdrjJXtENqGDGIVZsoXzDLgyiNFLQRq",
"name": "dami",
"last_name": "ola",
"first_name": "damilola",
"email": "[email protected]",
"wallet": "59a6d36bda891ea63bb35d49",
"modified_at": "2017-08-25T16:39:15.880Z",
"created_at": "2017-08-25T16:39:15.880Z",
"is_admin": false,
"school_details": {
"department": "59a0525481ee2c1ae2effd87",
"faculty": "59a0524381ee2c1ae2effd83",
"school": "59a0529681ee2c1ae2effd8b",
"set": 2017
},
"date_of_birth": "2017-08-25T16:39:15.880Z"
}
The school_details object has fields in it, but when I make it an entity it becomes
"school_details":{"checkIdOfTargetForPut":false,"debugRelations":false,"resolvedTargetId":0,"targetId":0,"virtualProperty":false}
Ok so I added @Id(assignable = true) to the UserSchoolDetails
Good news: I'm not getting the previous error
But I'm still getting this error
FATAL EXCEPTION: main
Process: com.pacent.smartalumni, PID: 13261
ava.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at io.objectbox.relation.ToOne.getTargetIdField(ToOne.java:248)
at io.objectbox.relation.ToOne.getTargetId(ToOne.java:236)
at io.objectbox.relation.ToOne.getTarget(ToOne.java:67)
at com.pacent.smartalumni.data.models.User.getUserSchoolDetails(User.java:130)
at com.pacent.smartalumni.utils.Utilities.updateUser(Utilities.java:56)
at com.pacent.smartalumni.presentation.signup.SignInActivity.onDataLoaded(SignInActivity.java:134)
at com.pacent.smartalumni.presentation.signup.SignInActivity.onDataLoaded(SignInActivity.java:34)
at com.pacent.smartalumni.data.remote.SignUpDataSource$1.onResponse(SignUpDataSource.java:37)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
This happens when it tries to invoke the getter
public UserSchoolDetails getUserSchoolDetails() {
return userSchoolDetails.getTarget();
}
We'll look into it later today.
@jirevwe Which JSON library do you use? Does it come with any build tool? Please also share how you build the object from JSON.
It seems that the JSON lib does NOT produce "valid" relation objects, so question is how it does the initialization.
I'm using Retrofit to make the REST calls and I know internally mine uses Gson.
Each object is parsed implicitly using Retrofit.
public void login(@NonNull final LoadDataCallback<UserResponse> callback, HashMap<String, Object> map) {
SignUpService signUpService = ServiceGenerator.createService(SignUpService.class);
signUpService.signUpUser(map).enqueue(new Callback<UserResponse>() {
@Override
public void onResponse(@NonNull Call<UserResponse> call, @NonNull Response<UserResponse> response) {
if (response.body() != null) {
if(response.body().getStatus().equalsIgnoreCase("success"))
callback.onDataLoaded(response.body());
else
Timber.e(response.body().getErr().toString());
}
}
@Override
public void onFailure(@NonNull Call<UserResponse> call, Throwable t) {
Timber.e(t);
callback.onDataNotAvailable();
}
});
}
UserResponse is just a wrapper for User to receive REST responses.
@jirevwe Please try again with V1.0.1 and post the new stack trace. It should be new because it's fail fast now if relation params are null.
Ok... will do...
So I updated it but I'm still getting the same error...
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at io.objectbox.relation.ToOne.getTargetIdField(ToOne.java:254)
at io.objectbox.relation.ToOne.getTargetId(ToOne.java:242)
at com.pacent.smartalumni.data.models.UserCursor.put(UserCursor.java:100)
at com.pacent.smartalumni.data.models.UserCursor.put(UserCursor.java:16)
at io.objectbox.Box.put(Box.java:396)
at com.pacent.smartalumni.utils.Utilities.updateUser(Utilities.java:58)
at com.pacent.smartalumni.presentation.signup.SignInActivity.onDataLoaded(SignInActivity.java:134)
at com.pacent.smartalumni.presentation.signup.SignInActivity.onDataLoaded(SignInActivity.java:34)
at com.pacent.smartalumni.data.remote.SignUpDataSource$1.onResponse(SignUpDataSource.java:37)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
@jirevwe Is there any update in this issue?? Because i am also facing it in same manner.
@jirevwe I just look into this issue and what i encountered is Retrofit2 is not aware of ToMany and ToOne type and also @greenrobot has already mentioned this thing in #104 .
I can share my model with the workaround. I am having a User Model which internally is related to Profile Picture Model coming from API Service and Location Model. I am using Retrofit2 to get the prepared User Model.
So my workaround in this issue is to provide @Transient annotation to ProfilePicture and Location type data members (as they will act as temporary data members and will not be stored into the db) and then add two new data members to the class as ToOne
@Entity
public class User {
@Id(assignable = true)
@Expose
@SerializedName("id")
private long id;
@Expose
@Transient
@SerializedName("location")
private Location location;
@Expose
@SerializedName("age")
private long age;
@Expose
@SerializedName("bio")
private String bio;
@Expose
@SerializedName("dob")
private String dob;
@Expose
@SerializedName("education")
private String education;
@Expose
@SerializedName("email")
private String email;
@Expose
@SerializedName("full_name")
private String fullName;
@Expose
@SerializedName("gender")
private String gender;
@Expose
@Transient
@SerializedName("profile_picture")
private ProfilePicture profilePicture;
@Expose
@SerializedName("username")
private String username;
@Expose
@SerializedName("verified")
private Boolean verified;
@Expose
@SerializedName("work")
private String work;
private ToOne<Location> locationRelation;
private ToOne<ProfilePicture> profilePictureRelation;
public long getId() {
return id;
}
public Long getAge() {
return age;
}
public String getBio() {
return bio;
}
public String getDob() {
return dob;
}
public String getEducation() {
return education;
}
public String getEmail() {
return email;
}
public String getFullName() {
return fullName;
}
public String getGender() {
return gender;
}
public String getUsername() {
return username;
}
public Boolean getVerified() {
return verified;
}
public String getWork() {
return work;
}
public Location getLocation() {
return location;
}
public ProfilePicture getProfilePicture() {
return profilePicture;
}
public ToOne<ProfilePicture> getProfilePictureRelation() {
return profilePictureRelation;
}
public ToOne<Location> getLocationRelation() {
return locationRelation;
}
public void setId(long id) {
this.id = id;
}
public void setAge(long age) {
this.age = age;
}
public void setBio(String bio) {
this.bio = bio;
}
public void setDob(String dob) {
this.dob = dob;
}
public void setEducation(String education) {
this.education = education;
}
public void setEmail(String email) {
this.email = email;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setUsername(String username) {
this.username = username;
}
public void setVerified(Boolean verified) {
this.verified = verified;
}
public void setWork(String work) {
this.work = work;
}
public void setLocation(Location location) {
this.location = location;
}
public void setProfilePicture(ProfilePicture profilePicture) {
this.profilePicture = profilePicture;
}
public void setLocationRelation() {
locationRelation.setTarget(location);
}
public void setProfilePictureRelation() {
profilePictureRelation.setTarget(profilePicture);
}
}
Before storing the user model into the objectbox just call these two methods. As these methods will set the relation between user and the below models.
E.g.
private void OnSuccessFullySignedIn(SignUpSignInResponse signUpSignInResponse) {
User user = signUpSignInResponse.getUser();
user.setLocationRelation();
user.setProfilePictureRelation();
boxStore.boxFor(User.class).put(user);
// Do the required after storing the data to ObjectBox.
}
I hope this could resolve you problem as well.
Hmm... Nice workaround.
Actually I already switched to another database.
I can close this issue now then...
@jirevwe A day before yesterday i also decided to rollback and change the database. But after watching all the issue in this repo i decided to take objectbox forward. Also best of luck
@rohitgupta1694 I swapped to this. https://www.couchbase.com/products/lite
I kept on head-butting walls with ObjectBox until I moved to a pure document based db.
Most helpful comment
@jirevwe I just look into this issue and what i encountered is Retrofit2 is not aware of ToMany and ToOne type and also @greenrobot has already mentioned this thing in #104 .
I can share my model with the workaround. I am having a User Model which internally is related to Profile Picture Model coming from API Service and Location Model. I am using Retrofit2 to get the prepared User Model.
So my workaround in this issue is to provide @Transient annotation to ProfilePicture and Location type data members (as they will act as temporary data members and will not be stored into the db) and then add two new data members to the class as ToOne and ToOne :
Before storing the user model into the objectbox just call these two methods. As these methods will set the relation between user and the below models.
E.g.
I hope this could resolve you problem as well.