ORMLite not loading child foreign fields - android

I'm using ORMLite 4.42 for an Android app. I have an entity which has foreign fields. These fields have foreign fields too. The problem is that when I get an element of the root entity, only the first level of foreign fields are loaded. The others levels are null.
On the database every seems ok. The id is correct. Any help?
Edit with models.
The Equipment model is always null when I query by ID. But if I query the whole table, then it gives me access to everything.
TABLE INCIDENT
#DatabaseField(generatedId=true)
private UUID id;
#DatabaseField(foreign=true, foreignAutoRefresh=true, canBeNull=false)
private UserEntity user;
#DatabaseField(dataType = DataType.DATE, canBeNull=true)
private Date date;
#DatabaseField(foreign=true, foreignAutoRefresh=true, canBeNull=true)
private EquipmentEntity equipment;
TABLE EQUIPMENT
#DatabaseField(generatedId=true)
private UUID id;
#DatabaseField(canBeNull=false, unique=true)
private String serial;
#DatabaseField(foreign=true, foreignAutoRefresh=true, canBeNull=false)
private EquipmentTypeEntity type;
TABLE EQUIPMENT TYPE
#DatabaseField(generatedId=true)
private UUID id;
#DatabaseField(canBeNull=true)
private String type;
#DatabaseField(foreign=true, foreignAutoRefresh=true, canBeNull=false)
private EquipmentModelEntity model;
TABLE EQUIPMENT MODEL
#DatabaseField(generatedId=true)
private UUID id;
#DatabaseField(canBeNull=false)
private String model;

I'm using ORMLite 4.42 for an Android app. I have an entity which has foreign fields. These fields have foreign fields too. The problem is that when i get an element of the root entity, only the first level of foreign fields are loaded. The others levels are null.
Right, this is by design. ORMLite specifically limits the number of times it auto-refreshes a sub-element. This was done to protect against huge object trees swallowing memory and against self referential objects.
To quote the docs for foreignAutoRefresh:
NOTE: To protect against recursion, there are a couple of places were auto-refreshing has been limited. If you are auto-refreshing a class that itself has field with foreignAutoRefresh set to true or if you are auto-refreshing a class with a foreign collection, in both cases the resulting field will be set to null and not auto-refreshed. You can always call refresh on the field directly if you need it.
NOTE: If you have an auto-refreshed field that is an object that also has an auto-refreshed field, you may want to tune the maxForeignAutoRefreshLevel value. See below.
To quote from the docs for maxForeignAutoRefreshLevel:
This can be used to set the maximum number of levels to configure foreign objects. For example, if you have a Question which has an foreign field of the best Answer, and the Answer has an foreign field to the corresponding question, then the configuration back and forth can get large. This is especially a problem with auto-refreshed fields when you lookup the Question it could cause an infinite loop. By default, ORMLite only goes through 2 levels but you can decrease it to 1 (0 is not valid) or increase it. The higher the number the more database transactions happen when you load in your Question.
If you increase the maxForeignAutoRefreshLevel to be more then it will issue the extra queries to refresh the elements.
#DatabaseField(foreign=true, foreignAutoRefresh=true, canBeNull=true,
maxForeignAutoRefreshLevel=3)
private EquipmentEntity equipment;

Related

Is it possible to create a table in the Room without a primary key?

I have a table in MySql and I named it FAQs and inside the table, There are two columns, Question column and Answer column, I want to get the data who inside the FAQs table and store it in the offline database but I got this message An entity must have at least 1 field annotated with #PrimaryKey
The Table
#Entity(tableName = "FAQs")
public class FAQModel {
private String question, answer;
public String getQuestion() {
return question;
}
public String getAnswer() {
return answer;
}
}
Is it possible to create a table in the Room without a primary key?
Yes you can, with some difficulty, but not via room annotation, and even still it would have a primary key so really the answer is No.
It is possible (e.g. via a callback) to create a table that does not appear to have a primary key column e.g. CREATE TABLE IF NOT EXISTS the_table (question TEXT, answer TEXT). However,
it would have a primary key on the column rowid which is normally hidden.
such a table would not be able to be readily used, as you would have to avoid Room's compilation time SQL statement checking.
you could also not take direct advantage of Room's underlying table to/from object handling.
However, you make the comment
But in the android app I only get the question and answer column and I am not getting faqId because I don't want to use it inside my app.
So you could have a POJO class that excludes the faqId column e.g.
class FAQModelLessIdColumn {
String question,answer;
}
Assuming that FAQModel is the entity and thus the tablename then with the above you could have an #Query such as:-
#Query("SELECT * FROM faqmodel")
abstract List<FAQModelLessIdColumn> getFAQModelsLessTheFaqIdColumn();
However, over time, you will probably learn that the primary key column, is highly beneficial as it MUST uniquely identify a single row in the table. So from a single value you can perform CRUD operations efficiently.
As an analogy consider the table as a list where the questions have been written down in no particular order. To find a question and it's answer How many days in a leap year then you would have to search the entire list until you found the question. If however the list was split into lists according to the first character of the question and these were maintained in order then by skipping to the H list would likely make finding the question easier. Thus indexes can greatly reduce search times. So having the faqId available enables searches via the primary index and they will be faster.

Migrating auto generate primary key

How can I migrate a primary key field, which was not set to Auto generate before?
From
#PrimaryKey
private int id;
To
#PrimaryKey(autoGenerate=true)
private int id;
Since Sqlite does not support altering columns, my only guess is to migrate the whole table as is and resetting the constraints.
Do I even have to migrate the database during the development process or can I just rebuild it, since my database will change rapidly, so I don't have to migrate every time?
I suggest you to change your approach: add an unique identifier (UID) as alternative way to identify records.
You can define a UID with annotation Entity on your POJO.
#Entity(indices={#Index(value="uid", unique=true)})
publi class Pojo {
..
public String uid;
..
}
When you insert a record in your database, you can define uid field using:
String uuid = UUID.randomUUID().toString();
You can use the UUID field to identify your records, in absolute way. When you migrate to a version to another, you don't work with the old ids, you can always work with UID.

Android Room: auto Increment field

Is there any way to set a field auto increment with android Room?
There is a table which contains 3 fields: id, name, order. And I want the field order to be an auto increment field.
#PrimaryKey(autoGenerate = true)
private long id;
private String name;
private int order;
Set field as primary key can achieve this, but there is already one id.
I can handle the order by myself, maybe set the field order as unique is much safer. But I prefer letting the db do it automatically. How can I do that?
Currently, Android doesn't support auto increment. Even for primary key, its not auto-increment, its auto-generate. It won't be serial numbers. It will generate a random hash numbers.
Auto generate is supported only for primary key but not for any normal column
#PrimaryKey(autoGenerate = true)
Annotate your Entity class with the code above.

No "NOT NULL" and "UNIQUE" constraint on Room Persistence Library

While playing with the Room Persistence Library I came to know that there is no methodology to set a data class field with NOT NULL and also UNIQUE constraints. whether SQLite supports those constraints. Isn't it a problem to migrate old database where those constraints are used? Can anyone give a suggestion for this issue?
I came to know that there is no methodology to set a data class field with NOT NULL and also UNIQUE constraints
A #NonNull annotation on an #Entity field will cause that field's column to have NOT NULL applied to it.
unique=true on an #Index will enforce a uniqueness constraint (e.g., #Entity(indices={#Index(value="something", unique=true)}). However, you are correct that a plain UNIQUE constraint on a column, other than via an index, is not supported.
Isn't it a problem to migrate old database where those constraints are used?
Room is not designed to support existing database structures, particularly in the now-current alpha state. Over time, I would expect Room to support a higher percentage of SQLite features, though I will be stunned if it ever reaches 100%.
Complementary answer about NOT NULL for those using Kotlin:
please note that marking a type as non optional will automatically make it not null (and an optional type will not do that).
You can check it in the schema generated by room with #Database(exportSchema = true) on your database.
For example I have something like that:
#Entity(tableName = "messages")
data class Message (
#PrimaryKey
val messageId: UUID = UUID.randomUUID(),
val date: Date = Date(),
val receivedDate: Date? = null
)
And in the generated schema I can read:
"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageId` TEXT NOT NULL, `date` INTEGER NOT NULL, `receivedDate` INTEGER, PRIMARY KEY(`messageId`))"
(Note: the Date type is here an Int and the UUID a string due to converters I use elsewhere)
If you have multiple item that is to be marked unique & based on that you want to insert in db then you can use composite primary key.
For Not null, Room has provided "#NonNull" annotation that is added over the field that cannot be null.
In the below mentioned eg. roll number is unique in each class but 2 different class can have same roll number. So we can use class & rollNumber as composite primary key & insert in db uniquely.
Example:
#Entity(primaryKeys = {"rollNumber", "class"})
class Student {
#NonNull
private int rollNumber;
private String firstName;
private String lastName;
private int class;
}
for a null able field you can use wrapper primitive type java. for example use Integer instance int in your Room Table.
as in wrapper primitive type java this can bee null but primitive type class cant bee null. and in generation of this SQL code for primitive field that use notNull=true but when use Integer in generayion SQL code use notNull=false

android ormlite - storing string array

I want to persist a class which contain String array.
How to do it in ormlite?
For example,
class A {
int age;
String[] childrenNames = new String[2];
}
First you make the class Serializable.
You can optionally add the table name at the top of the class by annotation.
then for the variables you have to add the database field annotation. In case of the string array you also have to annotate it as a Serializable datatype.
You will get something like this:
#DatabaseTable(tableName = "A")
Class A implements Serializable{
#DatabaseField
int age
#DatabaseField(dataType = DataType.SERIALIZABLE)
String[] childrenNames = new String[2];
}
Also dont forget to create getters and setters for each of the variables.
I want to persist a class which contain String array. How to do it in ormlite? For example,
You could store this as a serialized stream for sure.
However, a better way to do this is to use a ForeignCollection. ORMLite does not do the magic fu that other ORM libraries do to support arrays. Maybe it should. In the meantime, here are the docs on setting up another table for your children-names:
http://ormlite.com/docs/foreign-collection
One table would be for A. Another table would be for the ChildrenName. Each ChildrenName entity would have a foreign-field of A which would show which A each name corresponded to.
#Gray told the better way todo.
#ForeignCollectionField(eager = false)
ForeignCollection orders;
From docs:
In the above example, the #ForeignCollectionField annotation marks that the orders field is a collection of the orders that match the account.
The field type of orders must be either ForeignCollection or Collection – no other collections are supported because they are much heavier with many methods to support.

Categories

Resources