I have a model that has a one-to-many relationship of itself. Every time I run my android application, I keep getting an SQLException that states that my Foreign Collection class Route for my field childRoutes column-name does not contain a foreign field of class Route. I feel like I specified this already in my Model but I may be missing something?
Here is my model excluding the getters/setters:
public class Route implements Serializable{
#DatabaseField(id = true, canBeNull = false, columnName = "id")
private long id;
#DatabaseField(columnName = "parent", foreign = true)
private Route parent;
#ForeignCollectionField
private ForeignCollection<Route> childRoutes;
}
I followed this question here but I still can't seem to be getting it right.
ORMLITE one-to-many recursive relationship
I seemed to have solved the issue by replacing private ForeignCollection<Route> to just Collection<Route>. I'm not quite sure why it's not working if I use ForeignCollection as the type, but it seems to fix all my issues.
Related
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;
}
I want to add an Attachment entity which I will be refering to from multiple different Entities, but it does not refer back to these, how do I get this working in ORMLite?
I keep getting this Exception:
Caused by: java.sql.SQLException: Foreign collection class entity.Attachment for
field 'attachments' column-name does not contain a foreign field named
'attachmentId' of class enity.News
For example I have a News Entity
#DatabaseTable
public class News extends Record {
#DatabaseField(index = true, id = true)
private long newsArticleId;
#DatabaseField
private String subject;
#DatabaseField
private String content;
#ForeignCollectionField
Collection<Attachment> attachments;
}
The Attachment Entity:
#DatabaseTable
public class Attachment extends Record {
#DatabaseField(id = true, index = true)
private long attachmentId;
#DatabaseField
private String attachmentUrl;
}
Could someone please point to me and laugh and tell me why I am doing this wrong and what I'm misunderstanding here. Thanks.
This is a FAQ. To quote from the ORMLite docs on foreign-collections:
Remember that when you have a ForeignCollection field, the class in the collection must (in this example Order) must have a foreign field for the class that has the collection (in this example Account). If Account has a foreign collection of Orders, then Order must have an Account foreign field. It is required so ORMLite can find the orders that match a particular account.
In your example, for ORMLite to figure out which Attachments a particular News entity has, the Attachment entity must have a News field. The other way to do would be to have a join table, but ORMLite won't do that for you.
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.
When writing an instance of my data class to the database via ORMLite, and one of the child members (a foreign field) is null, I get back a non null child member.
Data classes as follows:
public class Site {
// snip
#DatabaseField(foreign = true, canBeNull = true)
private InstallationType installationType;
}
public class InstallationType {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField
private String name;
}
When I read my instance of the Site class again via
getSiteDao().queryForId(id);
the installationType member is non null, but with a non-existent id. The only way the rest of our application can now work with this object, is if I manually do a lookup through the InstallationTypeDAO and set what I get back on the site. Query will sometimes return null as per the documentation.
Is there a way of getting ORMLite to set this member to null?
This was a bug in ORMLite that was fixed in version 4.15 (3/7/2011). Here's the change log file. What version are you using? Have you tried to update? Here's the bug report page:
Currently the following test passes so I think we have good coverage on that bug.
#Test
public void testForeignNull() throws Exception {
Dao<Foreign, Integer> dao = createDao(Foreign.class, true);
Foreign foreign = new Foreign();
foreign.foo = null;
assertEquals(1, dao.create(foreign));
Foreign foreign2 = dao.queryForId(foreign.id);
assertNotNull(foreign2);
assertNull(foreign2.foo);
}
With Foreign having the following fields:
#DatabaseField(generatedId = true)
public int id;
#DatabaseField(foreign = true)
public Foo foo;
If you are up to date in versions, please let me know if you can change the test to get it to fail.
I am not clever from ORMlite documentation. Is is possible to declare in class, that this parameter is foreign key?
e.g. I have table Customer:
#DatabaseTable(tableName = "customer")
public class Customer {
#DatabaseField(id = true)
private String customerName;
#DatabaseField
private String customerSurname;
#DatabaseField(foreign = true)
private String accountNameHolder;
#DatabaseField
private int age;
public Customer() {
}
}
AccountNameHolder should aim towards DatabaseField name from table Accounts. How to do that? I have found only parameter foreign = true, but there is nothing about, which parameter and from which table it represents.
Thanks
AccountNameHolder should aim towards DatabaseField name from table Accounts. How to do that?
I'm not exactly sure what you want but possibly you should change your foreign field to be the actual type instead of a name:
#DatabaseField(foreign = true)
private Account account;
Internally, ORMLite will store a account_id field (maybe the string name) in the Customer table but you don't have to worry about that. Remember that when you query for a Customer, the Account that is set on the account field will just have the id field set. To have ORMLite also lookup the account you will need to set the foreignAutoRefresh=true.
As #Lalit pointed out, here is some documentation on this subject. We've spent a long time on the documentation so it should be helpful.
Foreign objects
Foreign auto refresh
Also, there is some example code about foreign fields.
Hope this helps.