I have API that returns a list of objects. These objects have relations to another objects. But API returns only IDs for them. In order to get full object I have to use another API. For instance:
class Owner extends RealmObject {
...
RealmList<Cat> cats;
}
class Cat extends RealmObject {
#PrimaryKey
String id;
String name;
}
so when I receive list of Owners I store them in database like this:
for (OwnerDto o : owners) {
Owner owner = new Owner();
...
RealmList<Cat> catsList = new RealmList<>();
for (Cat c : o.cats) {
Cat cat = new Cat();
cat.setId(c.id);
catsList.add(cat);
}
owner.setCats(catsList);
realm.copyToRealmOrUpdate(owner);
}
but in this case all the cats' names are deleted if they were populated before.
Is it possible to create RealmObject with ID only in order to set relation, and fill it with date afterwords ? Or specify relation with Id only ? Or any other solution ?
UPDATE:
I came up with solution like this:
for (OwnerDto o : owners) {
Owner owner = new Owner();
...
RealmList<Cat> catsList = new RealmList<>();
for (Cat c : o.cats) {
Cat cat = realm.where(Cat.class)
.equalsTo("id", c.id)
.findFirst();
if (cat == null) {
cat = new Cat();
}
cat.setId(c.id);
catsList.add(cat);
}
owner.setCats(catsList);
realm.copyToRealmOrUpdate(owner);
}
Here I figured out that there is method realm.objectForPrimaryKey(User.self, key: "key") for swift but I did not fund analog in java version.
It would be nice to have in this situation something like realm.getOrCreate(Cat.class, c.id) method. Is there any ?
It's pretty rough to do that if you don't have the whole objects before you want to link them together, because you can't just store a RealmList<T> of primitives, only RealmList<T extends RealmObject>.
So your options are:
1.) have the cats in your Realm and then download the Owner into which you piece it together with realm queries.
realm.beginTransaction();
Owner owner = new Owner();
owner.setCats(new RealmList<Cat>());
for(int i = 0; i < listOfCats.size(); i++) {
String id = listOfCats.get(i);
Cat cat = realm.where(Cat.class).equalTo("id", id).findFirst();
owner.getCats().add(cat);
}
realm.commitTransaction();
2.) have a RealmList<RealmString> in which you store the IDs.
public class RealmString extends RealmObject {
private String value;
public String getValue() { return value; }
public void getValue(String value) { this.value = value; }
}
You'll probably need to use some ugly queries afterwards.
RealmResults<Owner> ownerOfCat = realm.where(Owner.class)
.equalTo("catIds.value", catId).findAll();
Related
I have a RealmObject, which is used as a temporary data cache only (there will be many entries). I also wrote a static method add() so I can easily add a new entry, but it seems too complicated. Here is the whole class:
public class ExchangePairPriceCache extends RealmObject {
#Index
private String exchangeName;
#Index
private String baseCurrency;
#Index
private String quoteCurrency;
private float price;
private long lastPriceUpdate;
public ExchangePairPriceCache() {
exchangeName = "";
baseCurrency = "";
quoteCurrency = "";
price = 0;
lastPriceUpdate = 0;
}
public ExchangePairPriceCache(String exchangeName, String baseCurrency, String quoteCurrency) {
this.exchangeName = exchangeName;
this.baseCurrency = baseCurrency;
this.quoteCurrency = quoteCurrency;
price = 0;
lastPriceUpdate = 0;
}
public void setPrice(float price) {
// this needs to be called inside a Realm transaction if it's a managed object
this.price = price;
lastPriceUpdate = System.currentTimeMillis();
}
public float getPrice() {
return price;
}
/* static functions */
public static void add(String exchangeName, String baseCurrency, String quoteCurrency, float price) {
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(r -> {
ExchangePairPriceCache priceCache = r.where(ExchangePairPriceCache.class)
.equalTo("exchangeName", exchangeName)
.equalTo("baseCurrency", baseCurrency)
.equalTo("quoteCurrency", quoteCurrency).findFirst();
if(priceCache != null) {
priceCache.setPrice(price);
} else {
priceCache = new ExchangePairPriceCache(exchangeName, baseCurrency, quoteCurrency);
priceCache.setPrice(price);
ExchangePairPriceCache finalPriceCache = priceCache;
r.insert(finalPriceCache);
}
});
realm.close();
}
public static ExchangePairPriceCache get(String exchangeName, String baseCurrency, String quoteCurrency) {
Realm realm = Realm.getDefaultInstance();
ExchangePairPriceCache priceCache = realm.where(ExchangePairPriceCache.class)
.equalTo("exchangeName", exchangeName)
.equalTo("baseCurrency", baseCurrency)
.equalTo("quoteCurrency", quoteCurrency)
.greaterThan("lastPriceUpdate", System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(10)).findFirst();
if(priceCache != null)
priceCache = realm.copyFromRealm(priceCache);
realm.close();
return priceCache;
}
public static void deleteAll() {
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(r -> r.delete(ExchangePairPriceCache.class));
realm.close();
}
}
Questions:
Is this a good design (having static functions for ease of use)? I like how I can insert new entries into cache like ExchangePairPriceCache.add("NASDAQ", "AAPL", "USD", 100.5); and delete all with ExchangePairPriceCache.deleteAll() when needed.
How can I simplify add() function? Right now I check if entry already exists and then update the price and if it doesn't, I create a new object and insert it into Realm. I am not able to use updateOrInsert because I don't have unique index for object.
Maybe I am just questioning myself too much and this is all good as it is. But I'd really appreciate some input from experts who use it daily.
You should use a "Repository design pattern" with a DAO object (Data Access Object), to do all your read/ write transactions in realm.
Model class should be a blind copy of objects just holding entities.
Since you do not have any unique identifiers, you can try below
Cache the Exchange pair in Shared preferences file (if they are added earlier or not)
For faster read/writes : Create a temporary unique identifier with a combination of key-value pair that you already have
eg : (exchangeName + baseCurrency + quoteCurrency) - Cast into proper formats to create some unique key with all these values.
In the past a manually created as list of data, which was stored locally in a database. Now I would like to parse those data and put them through import json option into firebase dbs, but what I get doesn't look like firebase generated json.
What I get is:
[
{
"id":"id_1",
"text":"some text"
},
{
"id":"id_2",
"text":"some text2"
},
...
]
what I want is something like this:
{
"id_1": {
"text":"some text",
"id":"id_1"
},
"id_2":{
"text":"some text2",
"id":"id_1"
},
...
}
my Card class
class Card{
private String id;
private String text;
}
retrieving data
//return list of card
List<Card> cards =myDatabase.retrieveData();
Gson gson = new Gson();
String data = gson.toJson(cards);
So how I can I achieve this (it looks to me) dynamically named properties for json that look like those generated by firebase ?
EDIT:
I found that gson has FieldNamingStrategy interface that can change names of fields. But it looks to me it's not dynamic as I want.
EDIT2
my temp fix was to just override toString()
#Override
public String toString() {
return "\""+id+"\": {" +
"\"text\":" + "\""+text+"\","+
"\"id\":" +"\""+ id +"\","+
"\"type\":"+ "\""+ type +"\""+
'}';
}
Your "temp fix" will store each object as a string not a object.
You need to serialize an object to get that data, not a list.
For example, using a Map
List<Card> cards = myDatabase.retrieveData();
Map<Map<String, Object>> fireMap = new TreeMap<>();
int i = 1;
for (Card c : cards) {
Map<String, Object> cardMap = new TreeMap<>();
cardMap.put("text", c.getText());
fireMap.put("id_" + (i++), cardMap);
}
Gson gson = new Gson();
String data = gson.toJson(fireMap);
This question already has answers here:
Cannot retrieve field values from realm object, values are null in debugger
(5 answers)
Closed 5 years ago.
I need to do a simple query in Realm, retrieve a list of MyModel object and later use it somewhere else in my app. It happens that once I query Realm, each object has null values, but the toString returns the expected values.
Model:
#RealmClass
public class MyModel extends RealmObject implements Serializable {
public static final String KEY_MODEL = "key_myModel";
#PrimaryKey
private int id;
private String myStr;
private int myInt;
//.... getters and setters
#Override
public String toString() {
return "id = " + id
+ "\nmyStr = " + myStr
+ "\nmyInt = " + myInt;
}
}
How do I store the value:
public static void storeModel(MyModel model) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
realm.copyToRealm(model);
realm.commitTransaction();
}
How do I retrieve the objects:
public static RealmList<MyModel> getNewElementsFromIndex(int indexFrom) {
Realm realm = Realm.getDefaultInstance();
RealmResults<MyModel> allValues = realm.where(MyModel).greaterThan("id", indexFrom).findAll();
RealmList<MyModel> finalList = new RealmList<MyModel>();
finalList.addAll(allValues.subList(0, allValues.size()));
return finalList;
}
When i call getNewElementsFromIndex(value) i get a list of item, but all items in this list have the parameter myStr = null and myInt = 0.
What am I doing wrong?
For managed realm objects, data is not copied to the fields, you obtain them through the proxy getter/setter calls.
Therefore, the fact that fields are null and toString() shows the values is completely expected and well-documented behavior.
To see the values, you have to add watches for the getter methods.
See the documentation.
Check out following classes:
class Person{
int id;
String name;
RealmList<Mail> mails;
...
}
class Mail{
int id;
String content;
...
}
I have a Person object (ie: mPerson) and I am accessing all Mails of the Person object by mPerson.getMails(). Till here everything is cool.
Here is the question: Is there way to query over the returned list such as findAllSortedAsync()?
Just use RealmList.where() to create a query. You can find document here
For example:
RealmList<Mail> mails = person.getMails();
RealmResults<Mail> results = mails.where().equalTo("id", 1).findAllSortedAsync();
RealmResults<Contact> contacts = mRealm.where(Contact.class).findAll();
int size = contacts.size();
for (int i = 0;i<size;i++){
Contact contact = contacts.get(i);
RealmList<EMail> eMails = contact.emails;
}
I use ormlite and I have a db with a field:
public static final String NAME = "name";
#DatabaseField (canBeNull = false, dataType = DataType.SERIALIZABLE, columnName = NAME)
private String[] name = new String[2];
And I would like to get all elements that name[0] and name[1] are "car". I try to add a where clausule like:
NAMEDB nameDB = null;
Dao<NAMEDB, Integer> daoName = this.getHelper().getDao(NAMEDB.class);
QueryBuilder<NAMEDB, Integer> queryName = daoName.queryBuilder();
Where<NAMEDB, Integer> where = queryName.where();
where.in(nameDb.NAME, "car");
But it doesn't work because it's an array string.
I have other fields:
public static final String MARK = "mark";
#DatabaseField (canBeNull = false, foreign = true, index = true, columnName = MARK)
private String mark = null;
And I can do this:
whereArticulo.in(nameDB.MARK, "aaa");
How can I solve my problem? Thanks.
It seems to me that a third option to store a string array (String[] someStringArray[]) in the database using Ormlite would be to define a data persister class that converts the string array to a single delimited string upon storage into the database and back again to a string array after taking it out of the database.
E.g., persister class would convert ["John Doe", "Joe Smith"] to "John Doe | Joe Smith" for database storage (using whatever delimiter character makes sense for your data) and converts back the other way when taking the data out of the database.
Any thoughts on this approach versus using Serializable or a foreign collection? Anyone tried this?
I just wrote my first persister class and it was pretty easy. I haven't been able to identify through web search or StackOverflow search that anyone has tried this.
Thanks.
As ronbo4610 suggested, it is a good idea to use a custom data persister in this case, to store the array as a string in the database separated by some kind of delimiter. You can then search the string in your WHERE clause just as you would any other string. (For example, using the LIKE operator)
I have implemented such a data persister. In order to use it, you must add the following annotation above your String[] object in your persisted class:
#DatabaseField(persisterClass = ArrayPersister.class)
In addition, you must create a new class called "ArrayPersister" with the following code:
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.field.SqlType;
import com.j256.ormlite.field.types.StringType;
import org.apache.commons.lang3.StringUtils;
public class ArrayPersister extends StringType {
private static final String delimiter = ",";
private static final ArrayPersister singleTon = new ArrayPersister();
private ArrayPersister() {
super(SqlType.STRING, new Class<?>[]{ String[].class });
}
public static ArrayPersister getSingleton() {
return singleTon;
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
String[] array = (String[]) javaObject;
if (array == null) {
return null;
}
else {
return StringUtils.join(array, delimiter);
}
}
#Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
String string = (String)sqlArg;
if (string == null) {
return null;
}
else {
return string.split(delimiter);
}
}
}
Unfortunately ORMLite does not support querying fields that are the type SERIALIZABLE. It is storing the array as a serialized byte[] so you cannot query against the values with an IN query like:
where.in(nameDb.NAME, "car");
ORMLite does support foreign collections but you have to set it up yourself with another class holding the names. See the documentation with sample code:
http://ormlite.com/docs/foreign-collection