Persisting array of strings with greenDao - android

I'm trying to map an object to database with greenDao. But when it comes to arrays, I don't know how to do it. After receiving JSON from network and deserializing it with GSON, I have objects defined by this class:
public class Car {
Long carId;
String name;
ArrayList<String> listOfLinks;
}
In case of a a different architecture, like this:
public class Car {
Long carId;
String name;
ArrayList<Link> listOfLinks;
}
public class Link {
Long carId;
String link;
}
----
Entity cars = schema.addEntity("Car");
cars.addLongProperty("carId").primaryKey();
cars.addStringProperty("name");
Entity links = schema.addEntity("Link");
links.addStringProperty("name");
links.addIdProperty().primaryKey().notNull().autoincrement();
Property linkProperty = links.addLongProperty("carId").getProperty();
ToMany carToLinks = cars.addToMany(link, linkProperty);
It would is easy. Define some relations, define properties, add foreign key and your done. With arrays I have no clue what to do. Ideas?

That approach is not common when using relational databases.
This is commonly done using to-many relations : instead of using a list of String, you can create a Link entity and then use a list of Link.

Relation toMany is useful when you have a list of your not primitive object, that you can declare like entity that have its own id etc etc etc, and make list of entities (with toMeny). By doing that greenDao makes another table in the base for you new entity with the foreign key of the base entity that contains list. When you have list of primitive type the only way to do is to make converter that converts List into one of the primitive types that greenDao works naturally. You have to do something like this `
import org.greenrobot.greendao.converter.PropertyConverter;
import java.util.Arrays;
import java.util.List;
/**
*DOLE BREEE SQLITE BREEEEEE!!!**
*i choosed to convert List into one string
*that is going to be saved in database, and vice versa
*/
public class GreenConverter implements PropertyConverter, String> {
#Override
public List convertToEntityProperty(String databaseValue) {
if (databaseValue == null) {
return null;
}
else {
List<String> lista = Arrays.asList(databaseValue.split(","));
return lista;
}
}
#Override
public String convertToDatabaseValue(List<String> entityProperty) {
if(entityProperty==null){
return null;
}
else{
StringBuilder sb= new StringBuilder();
for(String link:entityProperty){
sb.append(link);
sb.append(",");
}
return sb.toString();
}
}
}
now above all the properties that are List you have to put
#Convert(converter=yourconverterclass.class, columnType = String.class)
#Entity
public class ShipEntry {
#Id(autoincrement = true)
private long ship_id;
private String name;
private String model;
private String manufacturer;
private String starship_class;
#Convert(converter = GreenConverter.class, columnType = String.class)
private List<String> pilots;
#Convert(converter = GreenConverter.class, columnType = String.class)
private List<String> films ;
}
you can create Converter as a inner class of entitiy, and in that case it has to be declared as staticthat is the only way i have found, but the bad side is that you can not use property that you are converting into query. There might me some typo, but i hope this helps to solve your problem

I also have the same issue, and there no answer (not in official docs, not in google). Please explain how to map List to Entity?
public class Car {
Long carId;
String name;
ArrayList<String> listOfLinks;
}
Can I do something like this?
#Entity(active = true, nameInDb = "CARS")
public class Car {
#Id
private Long id;
#NotNull
#Unique
private String remoteId;
#ToMany(joinProperties = {
#JoinProperty(name = "remoteId", referencedName = "carRemoteId")
})
private List<Links> listOfLinks;
}
#Entity(active = true, nameInDb = "LISTOFLINKS")
public class Links{
#Id
private Long id;
#NotNull
#Unique
private String remoteId;
#SerializedName("listOfLinks")
#Expose
private String listOfLinks;//is it possible?????
private String carRemoteId;
}

Since JPA 2.0, you can use an element collection to persist a Collection of value types. You just need to annotate the attribute with #ElementCollection and the persistence provider will persist the elements of the Collection in an additional database table.
#Entity
public class Author {
#ElementCollection
private List<String> phoneNumbers = new ArrayList<String>();
}
The element collection might seem easier to use than an entity with a one-to-many association. But it has one major drawback: The elements of the collection have no id and Hibernate can’t address them individually.
When you add a new Object to the List or remove an existing one, Hibernate deletes all elements and inserts a new record for each item in the List.
Let’s take a quick look at an example. The following code snippet selects an Author entity and adds a second phoneNumber to the element collection.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Author a = em.find(Author.class, 1L);
a.getPhoneNumbers().add("42424242");
em.getTransaction().commit();
em.close();
an element collection is an easy but not the most efficient option to store a list of value types in the database. You should, therefore, only use it for very small collections so that Hibernate doesn’t perform too many SQL statements. In all other cases, a one-to-many association is the better approach.

Related

Realm avoid updating nested object

I have an app that stores lots of data to work offline as well.
I have three classes, in a hierarchy like;
public class MainGroup
{
private UUID Oid;
private String name;
private Date CreatedOn;
}
-
public class Group
{
private UUID Oid;
private String name;
private Date CreatedOn;
private MainGruop MainGroup;
}
-
public class Product
{
private UUID Oid;
private String name;
private Date CreatedOn;
private MainGruop MainGroup;
private Group Group;
}
( Oid fields are selected as PrimaryKey with realm attribute. )
Let's say, all MainGroup objects were stored in Realm DB. Then, when i'm trying to insert Group objects, with nested MainGroup object but with only its Oid field to link its master, Realm updates the MainProduct record (with given Oid), and clear the other fields as nulls.
In same way, when i'm inserting Product objects and nested objects are includes only Oid, realm updates all fields with nulls.
So, there are more complex and deeply related objects and when i make a request to get JSON from server, i must produce a very big JSON response to keep data.
And mention to insert method; I'm creating java objects with JSON response via GSON and i'm using Realm.copyToRealmOrUpdate(obj); method to insert.
To reduce payload (JSON size, serialize and insertion process), i need to find a way to fix this issue.

DC2type on greenDao

I am using GreenDao for Android application, with some specification, for example, I have a Contact Model with some information like name, avatar, phone number, etc...
Right now the need is to change from only one phone number to a multiphone number.
Instead of creating two tables (table for numbers, and table for contacts), I really need just one information is the number so in my backend the contact numbers is stocked on a DC2type, (a json array saved as a string).
Do we have a possibility to do that using GreenDao?
i search for a solution or a DC2type implementation , etc ... and nothing is found
so i decide to created by my self , and this is what i did :
using the #Convert annotation presented of GreenDao 3 :
#Property(nameInDb = "phoneNumbers")
#Convert(converter = PhoneNumbersConverter.class, columnType = String.class)
private List<String> phoneNumbers;
static class PhoneNumbersConverter implements PropertyConverter<List<String>, String> {
#Override
public List<String> convertToEntityProperty(String databaseValue) {
List<String> listOfStrings = new Gson().fromJson(databaseValue,List.class);
return listOfStrings;
}
#Override
public String convertToDatabaseValue(List<String> entityProperty) {
String json = new Gson().toJson(entityProperty);
return json;
}
}
short story long , i create a json to array parser
thanks to myself to helped me :D

How to use Android Room on entities without fields?

I have java objects which are are backed by a HashMap and thus do not have plain fields which can be discovered via reflection. For example:
public class City {
private final HashMap<String, String> internalMap = new HashMap<String, String>();
public String getId() {
return internalMap.get("id");
}
public void setId(String id) {
internalMap.put("id", id);
}
public String getName() {
return internalMap.get("name");
}
public void setName(String name) {
internalMap.put("name", name);
}
}
I want to use classes such as this an entity in Room without having to change its structure since I have many such classes which are auto-generated using code generation tools and there are specific reasons why they need to be backed by a HashMap. Each value of the HashMap should end up as a column in my database table (the key String is an internal implementation detail). Is it possible? Seems not to be at the moment due to how fields are discovered by the annotation processor.
UPDATE:
None of the answers were at all what I was looking for. I opened a new question without mentioning HashMap since that detail was not supposed to be relevant but all the answers latched on to it. See How to use Android Room with POJOs provided by an external library? for the updated question.
You can use TypeConverter for this.
For instance, you can use serialization provided by GSON to easily convert the map to serialized state and vice versa.
class MapConverter {
#TypeConverter
public String fromMap(HashMap<String, String> map) {
return new Gson().toJson(map);
}
#TypeConverter
public HashMap<String, String> fromString(String serializedMap) {
Type type = new TypeToken<HashMap<String, String>>(){}.getType();
return gson.fromJson("serializedMap", type);
}
}
And in your entity class:
#Entity
#TypeConverters({MapConverter.class})
public class CityEntity {
//...
private final HashMap<String, String> internalMap;
//...
}
So the converter will be available for this entity to serialize the hashmap to string and vice versa.
Gson is just a possible solution, you can actually use whatever you want.
Room has it's own set of annotations - and if you do not want to have a field mapped, you have to indicate this towards the annotation processor by annotating the POJO as it may be required; eg. #Entity without assigning a tableName, as well the #Ignore above the field to ignore:
#Entity
public class City {
#Ignore
private final HashMap<String, String> internalMap = new HashMap<>();
/* concerning the second part of the question: */
public HashMap<String, Object> toHashMap() {
return this.internalMap;
}
}
besides those getters have the fundamental problem, that they assume all the keys would exist, despite the HashMap might not have been populated with all (or any) of those keys. and it's absurd to represent a city with two different types of object - while one has full control over which fields are being mapped and which are being ignored (one possible approach does not have to exclude the other here)... a method to return the HashMap might be useful, eg. in order to insert that into Firebase.
I'd rather go for fields holding the values, while nevertheless being able to return a HashMap:
#Entity(tableName = "cities")
public class City {
#ColumnInfo(name = "cityId")
#PrimaryKey(autoGenerate = true)
private int cityId = -1;
#ColumnInfo(name = "cityName")
private String cityName = null;
...
/* concerning the second part of the question: */
public HashMap<String, Object> toHashMap() {
HashMap<String, Object> values = new HashMap<>();
if(this.cityId != -1) {values.put( "id", this.cityId);}
if(this.cityName != null) {values.put("name", this.cityName);}
return values;
}
}
such a toHashMap() method also provides control, which fields the returned HashMap shall contain, while the example with the TypeConverter & GSON would (as it is) convert the whole HashMap.

How to store objects in Android Room?

Basically, there are two things I don't understand: objects with objects and objects with lists of objects
Say I receive a list of objects from the server. Each of them looks like this:
#Entity
public class BigObject {
#PrimaryKey
private int id;
private User user;
private List<SmallObject> smallObjects;
}
with these two objects as fields:
#Entity
public class User {
#PrimaryKey
private int id;
private String name;
#TypeConverters(GenderConverter.class)
public MyEnums.Gender gender;
}
#Entity
public class SmallObject {
#PrimaryKey (autoGenerate = true)
private int id;
private String smallValue;
}
They are more complicated than this, so I can't use #TypeConverters as Room suggests:
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
How do I store this data structure in Room?
I think the best way to answer this is a breif overview in storing structures...
Lists
Room does not support storing lists that are nested inside of a POJO. The recommended way to store lists is to use the foreign key approach. Store the List of objects in a seperate table (in this case a smallObjects table) with a foreign key to their related parent object (in this case "big_object_id"). It should look something like this...
#Entity
public class BigObject {
#PrimaryKey
private int id;
private User user;
#Ignore
private List<SmallObject> smallObjects;
}
#Entity(foreignKeys = {
#ForeignKey(
entity = BigObject.class,
parentColumns = "id",
childColumns = "big_object_fk"
)})
public class SmallObject {
#PrimaryKey (autoGenerate = true)
private int id;
private String smallValue;
#ColumnInfo(name = "big_object_fk")
private int bigObjectIdFk
}
Note that we have added the #Ignore annotaiton to List<SmallObject> as we want to ignore the field during Room persistance (as lists are not supported). It now exists so that when we request our list of related small objects from the DB we can still store them in the POJO.
To my knowledge this will mean you are making two queries.
BigObject b = db.BigObjectDao.findById(bOId);
List<SmallObject> s = db.smallObjectDao.findAllSOforBO(bOId);
b.setsmallObjects(s);
It appears that there is a short hand for this in the form of #Relation
Type Converters
These are for cases where you have a complex data structure that can be flattend without losing information, and stored in a single column. A good example of this is the Date object. A Date object is complex and holds a lot of values, so storing it in the database is tricky. We use a type converter to extract the milli representation of a date object and store that. We then convert the millis to a date object on the way out thus keeping our data intact.
Embedded
This is used when you want to take the fields of all nested POJOs in your parent POJO and flatten them out to store in one table. an example :
- name
- age
- location
- x
- y
- DOB
..when embedded this structure would be stored in the database as :
- name
- age
- location_x
- location_y
- DOB
In a sense Embedded exists to save you time creating type converters for every nested object that contains primary type fields like String, int, float, etc...
Convert Object/List<Object> to String and then, Store it.
You can store the objects in Room Library as String. For that, you can serialize the object and store it as String in the Room Database.
Store to Room
Object -> Serialize -> String -> Store
Read from Room
String -> Deserialize ->Object -> Read
How to Serialize/Deserialize?
There are many options available. You can either do it manually or you can use a library for this. You can use Google's GSON library. It is pretty easy to use.
Code: Object -> String
public String stringFromObject(List<YourClass> list){
Gson gson = new Gson();
String jsonString = gson.toJson(list);
return jsonString;
}
Code: String-> Object
public List<YourClass> getObjectFromString(String jsonString){
Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonString, listType);
return list;
}

Implementing a SQLite Database with List<String> as Column type using ORMLite

I have a requirement where I need to store a List in a column in the database. Serializing the list might be an option, but i am not sure if it is the right one.
Also, i want to avoid creating another table to store the list elements and a reference to the original table row.
I am using ORMLite for the database operations.
Its a concept of foreign collection.
You need to create an entity that wraps the String. Something looks like:
#DatabaseTable
public class Student {
#DatabaseField(generatedId = true)
print int id;
#DatabaseField
private String fname;
#DatabaseField
private String lname;
#DatabaseField(foreign = true)
private Address address;
}
Then your Address class would have a ForeignCollection of these Student.
#DatabaseTable
public class Address {
#DatabaseField(generatedId = true)
print int id;
#ForeignCollectionField()
private ForeignCollection<Student> student;
}
Also refer this link , may it will help you.

Categories

Resources