I decided to use Room for caching data and now because of the situation of the library that I developed, I need to hide some fields of my model and then give them to the client that use my library.
The model below had orderId and I added this because I need that but when I don't want to give this filled model with orderId. i know how to ignore fields in JSON. But how can i hide this one from my model and then give it to the client.
Do I make a mistake in using Room in the first place?
public class Participant {
#PrimaryKey
private long id;
#ColumnInfo(name = "order_id")
private long orderId;
private long threadId;
private String name;
private String firstName;
private String lastName;
For example :
i have a listener that is like the below
listener.add(participant);
i want to hide orderId first and then pass it to the listener.
Then in another class override this:
#Override
public void onAdd(Paticipant participant) {
super.onAdd(participant);
//here
}
One way to hide orderId from classes which use Participant, is to provide a getter for this variable and return null:
public Long getOrderId() {
return null;
}
We must change orderId to a Long in order for it to be set as null.
Additionally, you can override the toString() method to ignore orderId in any string representations of the class.
Use GSON library and create a new class for JSON model, without orderId:
class ParticipantJson {
final long id;
final long threadId;
final String name;
final String firstName;
final String lastName;
// Constructor
}
Then you can create JSON representation with:
ParticipantJson participant = new ParticipantJson(/* fields from Room model */);
Gson gson = new Gson();
String json = gson.toJson(participant);
USE A DIFFERENT MODEL FOR PRESENTATION!
Sorry for the caps but I cannot emphasize how important it is to use a different model for presentation.
Although you can hide fields from libraries like GSON or ROOM using keywords like transient or annotation like ignore you cannot hide a model attribute from class itself. Also remember that you cannot enforce a rule on a model that is not designed for the purpose.
TLDR; Create a new model and using a mapper map the Room model to this new presentation model.
Related
Thanks in advance.
I have scenario where i wanted to check the data difference between existing and new realm model object.
Example
public class PostModel extends RealmObject {
#Required
#PrimaryKey
#Index
private String postId;
private String message;
}
Let say we have two objects
Old
PostModel old = new PostModel("one", "Welcome");
realm.copyToRealm(old);
New Object
PostModel newOne = new PostModel("one", "Welcome to World");
before updating the old object with newOne should check data change, if change is there then should insert in the realm, like below
realm.dirtyCheckAndUpdate(old, newOne);
//underlying it should do below
Getting the record with id "one"
Check the difference between db record and new record (!old.message.equalsIgnore(newOne.message)).
if change is there then copyToRealmOrUpdate() should happen.
I just gave an example, i need to to this for complex RealmModel with relationship.
Why do you need to check? You can just call copyToRealmOrUpdate()? It will update data regardless, but if it overrides the data with the same data the end result is the same.
Otherwise, you will be forced to implement all the checking yourself, which is time-consuming and error-prone. You could also make your own annotation processor that generated the logic for you. It would look something like:
public boolean compare(PostModel m1, PostModel m2) {
if (!m1.getId().equals(m2.getId()) return false;
if (!m1.getMessage().equals(m2.getMessage()) return false;
if (!PostModelReference.compare(m1.getRef(), m2.getRef()) return false; // Recursive checks
}
I need to have a relatively large number of categories defined (about 30 at start, we'll be adding more). Consider this code:
public class Category {
public static final Category DRESS = new Category("Dress");
public static final Category SKIRT = new Category("Skirt");
...
private static final List<Category> CATEGORIES = Arrays.aslist(DRESS, SKIRT, ...);
private String name;
public Category(String name) {
this.name = name;
}
//Some static public method to iterate over categories
...
I need to have the categories declared and also need a way to iterate over them. I discard reflection because I think it's not a very good practice.
Is declaring a large name of static final fields of the same class and also having them inside a list a good practice? As an alternative, I thought about having a Map<Integer, Category> instead the list, and the fields were integers that would identify each category, so you would get the categories by getting them inside the map. Would this be better in terms of time and space performance?
PS: It's for an android project, if it changes something
Consider this code:
public class Category {
public static final Category DRESS = new Category("Dress");
public static final Category SKIRT = new Category("Skirt");
Yeah this is literally what enums do in the background, so
public enum Category {
DRESS("Dress"),
SKIRT("Skirt"),
...;
private String name;
private Category(String name) {
this.name = name;
}
// Category.values() returns the elements as an array
You should use enum instead of creating an object with new Category("Dress"); because creating an object is expensive than using enum. Java enums are implemented more like classes, so you can change your code seamlessly:
public enum Category {
DRESS("Dress"), SKIRT("Skirt");
private final String name;
Category(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Note:
The constructor for an enum type must be package-private or private access. It automatically creates the constants that are defined at the beginning of the enum body. You cannot invoke an enum constructor yourself.
Read more about enum at Enum Types
I would say using a List is good enough.
You should consider a Map only if you have to look up a
particular Category very frequently via some key property (like an int your case).
If There are no properties or methods in the Category class consider replace them with just Strings.
If new Categories are created at runtime and you want to persist them consider using a DB or File to save the Categories.
Edit: Answering the question in the comment
That would depend on the Category class. If its only purpose is to enumerate all the categories and the class itself does not have any other instance methods or properties then in terms of space complexity an Integer and your Category class is similar (since in a Map integer will be boxed in the Integer class object)
I would still suggest that you use a class called Category and a list if the purpose is only iterating over them and/or using specific instances of the Category class elsewhere in your application eg. Category.SOME_CATEGORY.
The following example is a good use-case
someMethod(Category category) {
// do something
}
versus
someMethod(int category) {
// before doing anything
// lookup this category by an int key
// in the the Map<Integer, Category>
}
The problem with the latter is that you could pass any int which may or may not be a valid key for a category. Using a class gives some bit for extra compile time check. Though you could always use an int def too. But again I would repeat that it all boils down to whether Category class has any instance methods or properties.
For small list, it is okay to use List or Map.
But for a large list, you may want to store them in a database.
Also ArrayList of String will be slightly efficient than using ArrayList of Category
I am building an Android Application, and I need to display information from different Objects in my ListAdapter.
E.g. I need to show patientName from Patient, and roomNumber from Room etc.
All my Objects are serialized from JSON-response(s).
At first I thought about creating a wrapper class, just for displaying the information in my List - but it seems like a crappy solution.
Like so:
public class ListOverviewWrapper {
final String firstName;
final String lastName;
final int roomNumber;
final String departmentName;
...
}
I can't seem to figure out, how to do this effectively. Any suggestions?
I've been trying to add Realm in my Android app. Their docs are pretty well explained & easy to follow. But it fails to explain this one particular area. I'm unable to figure out the practical use for the #Ignore annotation. I know that fields under this annotation are not persisted.
Can someone please share a few use cases. Also I wanted to know the scope of such fields. I mean, if I set an #Ignore field to some value, would that value be available to the other classes in my app for that particular launch session. If yes, then how do we access it? If no (which I guess is the case), then why do we need such a field anyway?
I've searched here and on web but couldn't find the relevant information. If out of my ignorance, I've missed upon some resource, please guide me to it.
Thanks.
Accordingly to the official documentation (see https://realm.io/docs/java/latest/) #Ignore is useful in two cases:
When you use GSON integration and your JSON contains more data than you want to store, but you still would like to parse it, and use right after.
You can't create custom getters and setter in classes extending RealmObject, since they are going to be overridden. But in case you want to have some custom logic anyway, ignored fields can be used as a hack to do that, because Realm doesn't override their getter & setters. Example:
package io.realm.entities;
import io.realm.RealmObject;
import io.realm.annotations.Ignore;
public class StringOnly extends RealmObject {
private String name;
#Ignore
private String kingName;
// custom setter
public void setKingName(String kingName) { setName("King " + kingName); }
// custom getter
public String getKingName() { return getName(); }
// setter and getter for 'name'
}
Ignored fields are accessible only from the object they were set in (same as with regular objects in Java).
UPDATE: As the #The-null-Pointer- pointed out in the comments the second point is out of date. Realm now allows having custom getters and setters in Realm models.
Here's a couple of real-world use cases:
1 - Get user's fullname:
public class User extends RealmObject {
private String first;
private String last;
#Ignore
private String fullName;
public String getFullName() {
return getFirst() + " " + getLast();
}
Get JSON representation of object:
public class User extends RealmObject {
private String first;
private String last;
#Ignore
private JSONObject Json;
public JSONObject getJson() {
try {
JSONObject dict = new JSONObject();
dict.put("first", getFirst());
dict.put("last", getLast());
return dict;
} catch (JSONException e) {
// log the exception
}
return null;
}
I've found it useful to define field names for when I am querying. For example
User.java
public class User extends RealmObject {
#Index
public String name;
#Ignore
public static final String NAME = "name";
}
And then later on I can do something like:
realm.where(User.class).equalTo(User.NAME, "John").findFirst();
This way if the schema changes from say name to id I don't have to hunt down every occurrence of "name".
Please see the the official documentation about #Ignore annotation:
The annotation #Ignore implies that a field should not be persisted to disk. Ignored fields are useful if your input contains more fields than your model, and you don’t wish to have many special cases for handling these unused data fields.
So, I have a few OrmLite 4.41 model classes which are initially being populated by GSON (simplified for clarity)
public class Crag {
#DatabaseField(id=true) private int id;
#ForeignCollectionField(eager=true, maxEagerLevel=2) #SerializedName("CragLocations")
private Collection<CragLocation> cragLocations;
}
public class CragLocation {
#DatabaseField(id=true) private int id;
#DatabaseField private int locationType;
#DatabaseField(foreign=true, foreignAutoCreate=true, foreignAutoRefresh=true)
private Crag crag;
#DatabaseField(foreign=true, foreignAutoCreate=true, foreignAutoRefresh=true)
private Location location;
}
public class Location {
#DatabaseField(id=true) private int id;
#DatabaseField private BigDecimal latitude;
#DatabaseField private BigDecimal longitude;
}
I'm then testing that things are happening as I expect...
#Test
public void canFindById() {
Crag expected = ObjectMother.getCrag431();
_repo.createOrUpdate(template431);
Crag actual = _repo.getCragById(431);
assertThat(actual, equalTo(template431));
}
and they aren't equal... why not? because in the object created by GSON (in ObjectMother.getCrag431()) the cragLocations field of Crag is an ArrayList and in that loaded by OrmLite it is an EagerForeignCollection
Am I missing a trick here? Is there a way to tell OrmLite what type I want that Collection to be? Should I just have a method that returns the collection as an arraylist and test for equality on that?
Thanks in advance
Is there a way to tell OrmLite what type I want that Collection to be?
There is no way to do this. When your Crag is returned by ORMLite, it is either going to be an EagerForeignCollection or LazyForeignCollection.
Should I just have a method that returns the collection as an arraylist and test for equality on that?
I assume in your Crag.equals(...) method, you are testing for equality for the cragLocations field as this.cragLocations.equals(other.cragLocations). This is not going to work because, as you guess, they are different types.
If you need to test equality you can extract both of them as an array. Something like:
Array.equals(
this.cragLocations.toArray(new CragLocation[this.cragLocations.size()]),
other.cragLocations.toArray(new CragLocation[this.cragLocations.size()]));