Per-class default property to register with on save instead of using registerObjectWithId manually
Hi. I've been using your library for a week now and I have to say I think it's great. It best suit my needs out of a few I looked over.
The main reason I switched over from Sugar was that, for my project, I need to have a few model classes with a String id property aside from a couple others with Long ids. The reason I need the String ids in the first place is I have some data that will be generated on the app locally and then shared across all devices running it as well with the use of WebSockets. This data being shared is of a permanent nature on the server so I'm generating version 1 UUIDs for records' ids values to help keep relationships among models consistent on all devices and server.
I wanted to have an annotation on a property to use as the identity field when saving the record. The problem with registerObjectWithId is my models usually have to-many or to-one relationships defined within them, and when calling save(), I have to manually call registerObjectWithId for each of them as well. After defining enough classes it starts to get hard to manage defining all their save() methods.
I'm now using this subclass for all my models:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface IdentityColumn {}
public class MyRushObject extends RushObject {
@RushIgnore
public boolean _didRegisterObjectId = false;
@RushIgnore
private String _identityValue;
private List<Field> getAllFields() {
List<Field> fields = new ArrayList<>();
for (Field field : getClass().getDeclaredFields()) {
fields.add(field);
}
for (Field field : getClass().getFields()) {
fields.add(field);
}
return fields;
}
public String getIdentityValue() {
if (identityValue == null) {
for (Field field : getAllFields()) {
if (field.isAnnotationPresent(IdentityColumn.class)) {
field.setAccessible(true);
try {
identityValue = field.get(this).toString();
} catch (IllegalAccessException ignore) {}
break;
}
}
}
return identityValue;
}
private void registerObjectWithIdentityRecursively(List<MyRushObject> done, MyRushObject record) {
if (done.contains(record))
return;
if (!record._didRegisterObjectId) {
String id = record.getIdentityValue();
if (id != null) {
RushCore.getInstance().registerObjectWithId(record, id);
record._didRegisterObjectId = true;
}
}
done.add(record);
for (Field field : record.getAllFields()) {
try {
field.setAccessible(true);
if (MyRushObject.class.isAssignableFrom(field.getType())) {
registerObjectWithIdentityRecursively(done, (MyRushObject) field.get(record));
} else if (field.isAnnotationPresent(RushList.class) && MyRushObject.class.isAssignableFrom(field.getAnnotation(RushList.class).classType())) {
List<MyRushObject> children = (List) field.get(record);
for (MyRushObject child : children) {
registerObjectWithIdentityRecursively(done, child);
}
}
} catch (IllegalAccessException ignore) {}
}
}
public void save() {
List<MyRushObject> done = new ArrayList<>();
registerObjectWithIdentityRecursively(done, this);
super.save();
}
}
class A extends MyRushObject {
@IdentityColumn
public String id;
...
}
This is working pretty well for me, but I imagine it may possibly become a performance concern down the road when my relationship network becomes more complex and I'm dealing with a good amount of data. I want to know if there might be a better way to implement something like this. Preferrably without the need for calling RushCore.getInstance().registerObjectWithId() so much.
Thanks.
Hi,
I wouldn't worry too much about calling
RushCore.getInstance().registerObjectWithId()
all it does is add the id to a Map that is read when saving, so will not effect performance.
There may well be a reason this is no use to you but there is a serialise and deserialise method on RushCore that will turn data to and from json, it handles all the ids stuff for you, the idea was to make data sharing easy - http://www.rushorm.com/advanced.html. RushObject default ids are already UUIDs so can be created across multiple clients without conflict. You would still need to register the ones with longs.
You do have a valid point that an annotation to define the id field would be pretty useful. Planning to add some features over the holidays for a 1.2 so will look at doing this.
Thanks
Thanks for the quick reply. I looked at the serializing/deserializing options available, but I'm already using a REST API with the JSONAPI format and can't really change that.
Again, great library and thanks.
In the meantime, if anyone finds this useful I've create a gist that I may or may not keep updated.