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.
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.
I am adding new entity FeedItem in my Room database and writing a migration for it.
Problem: I have a Date type in my FeedItem class, which is not primitive type. What is the proper way to write migration in this case?
#Entity(tableName = "FeedItem")
public class FeedItem implements Item, Votable {
private int id;
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "feedItemRowIndex")
private int rowIndex;
private int toId;
private int fromId;
private Date date;
...
my migration currently looks like this.
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(#NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE FeedItem (feedItemId INTEGER, " +
"feedItemRowIndex INTEGER, " +
"feedVotes INTEGER" +
"feedVote INTEGER" +
"toId INTEGER" +
"fromId INTEGER" +
"date Date" + // i need to change this row
...
"PRIMARY KEY (feedItemRowIndex))"
and here converter for Date type
public class DateConverter {
#TypeConverter
public static Date toDate(Long timestamp) {
return timestamp == null ? null : new Date(timestamp);
}
#TypeConverter
public static Long toTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
thanks!
If room.schemaLocation is set in your build.gradle file, then you can look at the generated schema and copy the exact sql that Room would use to create the table.
Complete example here
This was quite easy. Room generates primitive type appropriate to converter type.
So in my case type is INTEGER. Thanks to #EpicPandaForce for comment
Everywhere is mentioned, Realm needs setters and getters on private field members to work correctly. Accidentially I used public members without setters / getters and the small example worked. Do I miss something (now or in future), where this approach wouldn't work anymore?
Is use Android Studio with io.realm:realm-gradle-plugin:0.91.0
Here is some code:
public class Contact extends RealmObject {
public String phone;
public String mail;
public String person;
}
and later ...
realm.beginTransaction();
Contact contact = realm.createObject(Contact.class);
contact.mail="123";
contact.person="456";
contact.phone="789";
realm.commitTransaction();
final RealmResults<Contact> contacts = realm.where(Contact.class).findAll();
for (Contact c: contacts) {
Log.i(TAG, "mail: " + c.mail);
Log.i(TAG, "person: " + c.person);
Log.i(TAG, "phone: " + c.phone);
}
Emanuele from Realm here. Realm has been supporting public fields with no accessors since 0.88.0 https://realm.io/news/realm-java-0.88.0/
I'm new to Firebase, and I've been really enjoying it so far. I'm running into a problem; I'm using the FirebaseListAdapter similar to the tutorial outline here: https://github.com/firebase/AndroidChat
To use the FirebaseListAdapter, I need to use data model objects (to get the automatic binding to work nicely). The problem is I also want to keep a timestamp value with that model object, and I want to get the timestamp from the Firebase server.
What I have currently that is NOT working is a class DataModelObject (similar to com.firebase.androidchat.Chat in the demo example) with a constructor like :
DataModelObject(String data1, String data2, Map enQTimeStamp)
which I then try to use like this:
DataModelObject dmo = new DataModelObject ("foo", "bar", ServerValue.TIMESTAMP);
myFirebaseRef.push().setValue(dmo);
This causes a JsonMappingException when I try to run that code. I found a code snippet here :
https://www.firebase.com/blog/2015-02-11-firebase-unique-identifiers.html
But it's worthwhile to note that on line 4 of the Android code example, that will cause a compile time error (as he is trying to put ServerValue.TIMESTAMP into a Map, and TIMESTAMP is a Map itself)
What is the right way to do this and maintain compatibility with FirebaseListAdapter?
This sounds similar to this question: When making a POJO in Firebase, can you use ServerValue.TIMESTAMP?
When creating POJOs used to store/retrieve data apart from the default empty constructor I usually use a constructor similar to this:
Param param1;
Param param2;
HashMap<String, Object> timestampCreated;
//required empty constructor
public DataObject(){}
public DataObject(Param param1, Param param2) {
this.param1 = param1;
this.param2 = param2;
HashMap<String, Object> timestampNow = new HashMap<>();
timestampNow.put("timestamp", ServerValue.TIMESTAMP);
this.timestampCreated = timestampNow;
}
Be sure to include a getter for the HashMap<> used to store the Timestamp:
public HashMap<String, Object> getTimestampCreated(){
return timestampCreated;
}
Then use the #Exclude annotation to create a getter that you can use in your code to get the value of the timestamp if you need it. The #Exclude annotation will cause Firebase to ignore this getter and not look for a corresponding property
#Exclude
public long getTimestampCreatedLong(){
return (long)timestampCreated.get("timestamp");
}
Here's how I do it
//member variable
Object createdTimestamp;
public YourConstructor(){
createdTimestamp = ServerValue.TIMESTAMP
}
#Exclude
public long getCreatedTimestampLong(){
return (long)createdTimestamp;
}
Your db object should include these:
public class FirebaseDbObject {
private final Object timestamp = ServerValue.TIMESTAMP;
//........
//........
Object getTimestamp() {
return timestamp;
}
#Exclude
public long timestamp() {
return (long) timestamp;
}
}
This will add an extra field called "timestamp" to your object.
Edit: The answer posted by MobileMon is not fully correct as it does not have getter method. This is the complete and correct answer.
Kotlin provides an easy way to achieve this by data classes. You can create it like
data class FirebaseRequestModel(
var start_time: Any = ServerValue.TIMESTAMP,
var stop_time: Long = 0,
var total_time: Long = 0,
)
and use it directly by
val firebaseModel = FirebaseRequestModel()
firebaseRef.setValue(firebaseModel)
This will get default values from data class.
Or even you can initiate your own values by
val firebaseModel = FirebaseRequestModel(ServerValue.TIMESTAMP, 2134, 0)
firebaseRef.setValue(firebaseModel)
Similar to Urgurcan's answer, but a bit cleaner so the caller doesn't have trouble guessing between getTimestamp vs timestamp.
public class FirebaseDbObject {
private Object timestamp = ServerValue.TIMESTAMP;
//........
//........
#PropertyName("timestamp")
Object getRawTimestamp() {
return timestamp;
}
#Exclude
public long getTimestamp() {
return (long) timestamp;
}
}
You can do it:
public class MyTimeStamp {
private Object timestamp;
public MyTimeStamp() {
}
public Object getTimestamp() {
return timestamp;
}
public void setTimestamp(Object timestamp) {
this.timestamp = timestamp;
}
}
And so:
public static void start(Context context) {
MyTimeStamp timeStamp = new MyTimeStamp();
timeStamp.setTimestamp(ServerValue.TIMESTAMP);
Log.d(TAG, "start: ", timeStamp.getTimestamp().toString());
}
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