The essence of POJO class - android

please i am new to android, and i need to fully understand the use of POJO class for populating recyclerview in android. The way i do it is fetch data from local/API put it in a 2D ArrayList and pass it to the adapter class of recyclerview, for example the code below fetches music from device and add it to a musics 2D ArrayList :
musics.clear();
musicResolver = getActivity().getContentResolver();
Uri musicuri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicuri, null, null, null, "LOWER(" + MediaStore.Audio.Media.TITLE + ")ASC");
if (musicCursor != null) {
while (musicCursor.moveToNext()) {
ArrayList<String> tempmusic = new ArrayList<>();
tempmusic.add(0, musicCursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
long time = Integer.parseInt(musicCursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
tempmusic.add(1, (new SimpleDateFormat("mm:ss", Locale.getDefault())).format(new Date(time)));
tempmusic.add(2, musicCursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));
tempmusic.add(3, musicCursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
musics.add(tempmusic); //2d array
}
musicCursor.close();
}
rvAdapter.notifyDataSetChanged();
I have no problem with the above approach, it works fine, but i came across a tutorial where a POJO class was used which implements Serializable, see below:
public class Audio implements Serializable {
private String data;
private String title;
private String size;
private String duration;
public Audio(String data, String title, String duration, String size) {
this.data = data;
this.title = title;
this.duration = duration;
this.size = size;
}
public String getData() {
return data;
}
public String getTitle() {
return title;
}
public String getDuration() {
return duration;
}
public String getSize() {
return size;
}
}
Method for retrieving using the POJO class:
//musics.clear();
musicResolver = getActivity().getContentResolver();
Uri musicuri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicuri, null, null, null, "LOWER(" + MediaStore.Audio.Media.TITLE + ")ASC");
if (musicCursor != null) {
while (musicCursor.moveToNext()) {
ArrayList<String> tempmusic = new ArrayList<>();
String data =
cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
String title =
cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
String album =
cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
String artist =
cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));
tempmusic.add(new Audio(data, title, album, artist));
}
musicCursor.close();
}
rvAdapter.notifyDataSetChanged();
Now my question is:
What is the usefulness of the pojo class, if i can achieve the whole operation with 2d arraylist.
If pojo class implements serializable, what does it mean and the advantage(s) it has over using an ordinary pojo class or just using a 2d arraylist
A clear explanation will be very much appreciated, thanks.

What is the usefulness of the pojo class
The term "POJO" initially denoted a Java object which does not follow any of the major Java object models, conventions, or frameworks; nowadays "POJO" may be used as an acronym for "Plain Old JavaScript Object" as well, in which case the term denotes a JavaScript object of similar pedigree
A POJO is usually simple so won't depend on other libraries, interfaces or annotations. This increases the chance that this can be reused in multiple project types
Read more about Plain old Java object (POJO) and from here Advantage of POJO
If pojo class implements serializable, what does it mean and the advantage(s) it has over using an ordinary pojo class or just using a 2d arraylist
Serializable
Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.
Read serialization - advantages and disadvantages

Related

Insert a nested JSON array into SQLite after parsing with Gson

After parsing a nested JSON array with Gson, I now need to insert the result into SQLite. I tried inserting as done when not parsing with Gson, but that didn't work. I looked for ways to do that but couldn't find a solution.
The JSON parsing:
Gson gson = new Gson();
Type listType = new TypeToken<List<Country>>(){}.getType();
List<Country> countriesList = gson.fromJson(jsonString, listType);
for(Country country : countriesList) {
ContentValues insertValues;
}
If I wasn't using Gson, I would have written the line:
JSONObject countryObject = countriesList.getJSONObject(country);
EDIT
One of the objects from the JSON
[
{
"name":"Afghanistan",
"topLevelDomain":[
".af"
],
"callingCodes":[
"93"
],
"capital":"Kabul",
"region":"Asia",
"subregion":"Southern Asia",
"population":27657145,
"latlng":[
33.0,
65.0
],
"demonym":"Afghan",
"area":652230.0,
"gini":27.8,
"timezones":[
"UTC+04:30"
],
"nativeName":"افغانستان",
"numericCode":"004",
"currencies":[
{
"name":"Afghan afghani",
"symbol":"؋"
}
],
"languages":[
{
"name":"Pashto",
"nativeName":"پښتو"
},
{
"name":"Uzbek",
"nativeName":"Oʻzbek"
},
{
"name":"Turkmen",
"nativeName":"Türkmen"
}
],
"translations":{
"de":"Afghanistan",
},
"flag":"https://restcountries.eu/data/afg.svg",
"cioc":"AFG"
},
The model classes I wrote are only for the variables objects and arrays I needed.
The model class Country.Java
public class Country implements Parcelable {
private String name;
private String capital;
private String region;
private String subregion;
private int population;
private List<Double> latlng = new ArrayList<Double>();
private double area;
private double gini;
private List<String> timezones = new ArrayList<String>();
private List<Currency> currencies = new ArrayList<Currency>();
private List<Language> languages = new ArrayList<Language>();
private String flag;
public Country() {}
//getters, setters, toString() and Parcelable methods
}
The model class Currency.Java
public class Currency implements Parcelable {
private String name;
private String symbol;
//getters, setters, toString() and Parcelable methods
}
The model class Language.Java
public class Language implements Parcelable {
private String name;
private String nativeName;
//getters, setters, toString() and Parcelable methods
}
First get property of Country and then put it to content values and insert.
List<Country> countries = gson.fromJson(jsonString, new TypeToken<List<Country>>(){}.getType());
for(Country country : countries) {
String name = country.getName();
String capital = country.getCapital();
String region = country.getRegion();
String currencies = gson.toJson(country.getCurrencies());
...
ContentValues insertValues = new ContentValues();
insertValues.put("name", name)
insertValues.put("capital", capital);
insertValues.put("region", region);
insertValues.put("currencies", currencies);
...
long res = db.insert(TABLE_NAME, null, insertValues);
}
It's hard to know where your problem is without more code. You are doing two different operations:
unmarshalling a json
persisting data in SQLite.
Unmarshalling json with Gson
Inside the for-loop, can you log each country to see that you are getting a valid object with all the fields set? It is very much possible that you need to create a factory yourself. Does Country have subtype that you need to register through RuntimeTypeAdapterFactory? Maybe something like
final RuntimeTypeAdapterFactory<City> typeFactory = RuntimeTypeAdapterFactory
of(City.class, "MyCity")
.registerSubtype(S...,...)
Save in SQLite
Once you have valid data, then you must convert per field like so
public static ContentValues getContentValues(Country country) {
ContentValues values = new ContentValues();
values.put(COLUMN_ID, country.getId());
values.put(COLUMN_NAME, country.getName());
...
return values;
}
And then, if it still doesn't work, you will need to look at your SQLite schema.

Realm: updateOrInsert without index

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.

Realm query with List

I'm using realm to store my data on Android. Awesome framework! Now the only problem I'm now having is:
I got a array list strings with id's of Countries in my database.
Now I retrieve my Drinks that contains a relationship to countries.
Is there a way that I could to do a query like this:
String [] ids;
realm.where(Drinks.class).equalsTo("country.id", ids);
Something like that?
Or do I really need to do a query to get me all drinks and then filter the list manually?
EDIT:
My classes:
public class Drinks extends RealmObject {
#PrimaryKey
private String id;
private String name;
private Country country;
}
public class Country extends RealmObject {
#PrimaryKey
private String id;
private String name;
}
What you want to do is possible with link queries in theory (searching for "country.id"), however link queries are slow. Also you'd need to concatenate a bunch of or() predicates together, and I would not risk that with a link query.
I would recommend using the following
public class Drinks extends RealmObject {
#PrimaryKey
private String id;
private String name;
private Country country;
#Index
private String countryId;
}
public class Country extends RealmObject {
#PrimaryKey
private String id;
private String name;
}
And when you set the Country in your class, you also set the countryId as country.getId().
Once you do that, you can construct such:
RealmQuery<Drinks> drinkQuery = realm.where(Drinks.class);
int i = 0;
for(String id : ids) {
if(i != 0) {
drinkQuery = drinkQuery.or();
}
drinkQuery = drinkQuery.equalTo("countryId", id);
i++;
}
return drinkQuery.findAll();
Since the Realm database has added RealmQuery.in() with the version 1.2.0
I suggest using something like this.
//Drinks
public class Drinks extends RealmObject {
#PrimaryKey
private String id;
private String name;
private String countryId;
//getter and setter methods
}
//Country
public class Country extends RealmObject {
#PrimaryKey
private String id;
private String name;
//getter and setter methods
}
The code to use inside activity/fragments to retrieve drink list
String[] countryIdArray = new String[] {"1","2","3"} //your string array
RealmQuery<Drinks> realmQuery = realm.where(Drinks.class)
.in("countryId",countryIdArray);
RealmResults<Drinks> drinkList = realmQuery.findAll();
In latest version of Realm 7+, you can use anyOf to match a field against a list of values.
anyOf("name", new String[]{"Jill", "William", "Trillian"})
in older versions, use in instead of anyOf and with kotlin use oneOf instead of in.
see this issue
To match a field against a list of values, use in. For example, to find the names “Jill,” “William,” or “Trillian”, you can use in("name", new String[]{"Jill", "William", "Trillian"}). The in predicate is applicable to strings, binary data, and numeric fields (including dates).
Doc.-> https://realm.io/docs/java/latest#queries

Leverage ORMLite to map an Android Cursor into a POJO?

Since ORMLite is already mapping an Android Cursor to a POJO.
I'm wondering if I could somehow leverage that instead of writing a copy constructor and manually map a Cursor to my POJO?
I could for example, use a content provider and map that result into the same POJO's backed by ORMLite's Dao.
Perform operations on my dataset and then batch insert that data into my database using ORMLite.
The advantage to this would be that I don't have to write all the CRUD ofc.
EDIT:
this is a fabricated example but the concept is here. What would be the best way to do something like this?
#DatabaseTable(tableName = "my_dict")
public class MyDictionary{
#DatabaseField(columnName = COLUMN_ID, generatedId = true)
Integer _ID;
#DatabaseField(columnName = "word")
String word;
#DatabaseField(columnName = "app_id")
String app_id;
#DatabaseField(columnName = "frequency")
Integer frequency;
#DatabaseField(columnName = "locale")
String locale;
public MyDictionary(){}
}
public void someCoolDatasetOperation(){
Dao<MyDictionary, Long> dao = databaseHelper.getDao(MyDictionary.class);
List<MyDictionary> inDb = dao.queryForAll();
Cursor dictCursor = getContentResolver().query(WEB_SERVICE_URI,
null, // The columns to return for each row
null, // Selection criteria
null, // Selection criteria
null); // The sort order for the returned rows
//iffy part don't know the correct/best way to do this?????
AndroidDatabaseResults fabricatedResult = new AndroidDatabaseResults(dictCursor, null);
List<MyDictionary> fromContentProvider = dao.mapSelectStarRow(dictCursor);
//Do Something with the lists
}
I don't know a whole lot about ORMLite and I'm not positive the two will get along, but I'm working on library that can generate a POJO wrapper for cursor using an annotated class like you have there.
So if I were to try and use this library with your above example it might look like this:
#Entity
public class MyDictionary{
#Attribute(sqliteName = COLUMN_ID, primaryKey = true, autoIncrement = true)
Integer _ID;
#Attribute(sqliteName = "word")
String word;
#Attribute(sqliteName = "app_id")
String app_id;
#Attribute(sqliteName = "frequency")
Integer frequency;
#Attribute(sqliteName = "locale")
String locale;
}
public void someCoolDatasetOperation(){
Dao<MyDictionary, Long> dao = databaseHelper.getDao(MyDictionary.class);
List<MyDictionary> inDb = dao.queryForAll();
Cursor dictCursor = getContentResolver().query(WEB_SERVICE_URI,
null, // The columns to return for each row
null, // Selection criteria
null, // Selection criteria
null); // The sort order for the returned rows
List<MyDictionary> fromContentProvider = new ArrayList<MyDictionary();
while(dictCursor.moveToNext()) {
//MyDictionaryEntity is a generated class that contains getters and
//setters for all the attributes in MyDictionary
fromContentProvider.add(new MyDictionaryEntity(dictCursor));
}
}
I'm still actively developing it and haven't got around to too much documentation yet, but if you wanted to check it out:
https://github.com/adecker89/Glowplug

ORMLite where clausule in String Array

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

Categories

Resources