I have successfully created table with ORMLite, where it looks like it properly added uuid column as an primary key, index, etc.
public class Stat {
#DatabaseField(id = true)
protected UUID uuid = UUID.randomUUID();
...
Now, I'd like to be able to use full power of DAO provided and do (Stat is my class to be persisted, getUUID() returns UUID):
Stat statClassInstance = new Stat();
RuntimeExceptionDao<Stat, Integer> statDao = getHelper().getStatDataDao();
statDao.deleteById(statClassInstance.getUUID());
Compiler is giving me an error:
The method deleteById(Integer) in the type RuntimeExceptionDao<Stat,Integer> is not applicable for the arguments (UUID)
What I'm missing is how to use UUID ID's in methods such as deleteById, which accept integer.
I've read that UUID as ID was incorporated into ORMLite, but no mention if it went only as far as enabling them to be primary keys, not supporting all those helper methods (queryForId, deleteIds) etc.
In order to use the deleteById(ID) method the Dao<T,ID> should be created accordingly with corresponding parameters which have been identified in your T class. The ID will be interpreted as any type you define in your T class as a primary key. In this particular case it is UUID type and looking at the exception the DAO has been created using Dao<Stat, Integer> and should have been created as follows:
Dao<Stat, UUID> statDao = DaoManager.createDao(connSource, Stat.class);
hope this helps
Related
I updated my AbstractFooEntity class by adding an integer field like below, and I bumped the DB version (the DB is initialized with new DatabaseSource(context, Models.DEFAULT, DB_VERSION).
#Entity
abstract class AbstractFooEntity {
// this was in DB schema v1
String someField;
// added in DB schema v2
int newField = 0;
}
When I deploy this code and the (automatic) DB migration is performed when the user runs the new version of the Android app, I get the following error at runtime: "Cannot add a NOT NULL column with default value NULL".
What's the proper way to annotate the entity so that the framework correctly handles the automatic DB migration in this scenario?
I found a solution of this.
database.execSQL("ALTER TABLE table_name ADD COLUMN colum_name INTEGER DEFAULT 1 not null");
add the command : "DEFAUT value" after data type will solve your problem.
There are two options, first one is probably to be preferred - in the second one, you need to handle possible nullpointers in the code:
option 1
#Entity
abstract class AbstractFooEntity {
...
#Column(value = "0")
int newField;
}
option 2
#Entity
abstract class AbstractFooEntity {
...
Integer newField;
}
Let's assume we have following entities:
Item:
class Item {
...
#Index(unique=true)
private String guid;
...
#ToMany
#JoinEntity(entity = JoinItemsWithTags.class, sourceProperty = "itemGuid", targetProperty = "tagName")
private List<Tag> tagsWithThisItem;
...
}
Tag:
class Tag {
#Id
private Long localId;
#Index(unique = true)
private String name;
...
}
and we need to join them. Here is my join entity class:
#Entity(nameInDb = "item_tag_relations")
class JoinItemsWithTags {
#Id
private Long id;
private String itemGuid;
private String tagName;
...
}
I want to use tag name as a join property instead of Long id, because it's easier to support consistency when syncing with server.
But currently tags getter in Item class always return an empty list. I've looked into log and found generated query which using internally in that getter:
SELECT * <<-- there were a long sequence of fields
FROM "tags" T JOIN item_tag_relations J1
ON T."_id"=J1."TAG_NAME" <<-- here is the problem, must be `T."NAME"=J1."TAG_NAME"`
WHERE J1."ITEM_GUID"=?
So the problem is that join is base on tag's _id field. Generated List<Tag> _queryItem_TagsWithThisItem(String itemGuid) method implicitly uses that id to make a join:
// this `join` nethod is overloaded and pass tag's id as source property
queryBuilder.join(JoinItemsWithTags.class, JoinItemsWithTagsDao.Properties.TagName)
.where(JoinItemsWithTagsDao.Properties.ItemGuid.eq(itemGuid));
Correct approach is this case might be following, I suppose:
// source property is passed explicitly
queryBuilder.join(/* Desired first parameter -->> */ TagDao.Properties.Name,
JoinItemsWithTags.class, JoinItemsWithTagsDao.Properties.TagName)
.where(JoinItemsWithTagsDao.Properties.ItemGuid.eq(itemGuid));
But this code is in generated dao, and I don't know how to do anything with it. Is there any way to workaround this?
I have just started working with Room and although everything seems to be pretty intuitive I currently don't really understand how exactly I could handle relationships.
Because SQLite is a relational database, you can specify relationships between objects. Even though most ORM libraries allow entity objects to reference each other, Room explicitly forbids this. Even though you cannot use direct relationships, Room still allows you to define Foreign Key constraints between entities.(Source: https://developer.android.com/topic/libraries/architecture/room.html#no-object-references)
How should you model a Many to Many or One to Many Relationship?
What would this look like in practice (example DAOs + Entities)?
You can use #Relation annotation to handle relations at Room.
A convenience annotation which can be used in a Pojo to automatically
fetch relation entities. When the Pojo is returned from a query, all
of its relations are also fetched by Room.
See document.
(Google's document has confusing examples. I have written the steps and some basic explanation at my another answer. You can check it out)
I created a simple Convenience Method that populates manually a one to many relationship.
So for example if you have a one to many between Country and City , you can use the method to manually populate the cityList property in Country.
/**
* #param tableOne The table that contains the PK. We are not using annotations right now so the pk should be exposed via a getter getId();
* #param tableTwo The table that contains the FK. We are not using annotations right now so the Fk should be exposed via a getter get{TableOneName}Id(); eg. getCountryId();
* #param <T1> Table One Type
* #param <T2> Table Two Type
* #throws NoSuchFieldException
* #throws IllegalAccessException
* #throws NoSuchMethodException
* #throws InvocationTargetException
*/
private static <T1, T2> void oneToMany(List<T1> tableOne, List<T2> tableTwo) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
String tableOneName = tableOne.get(0).getClass().getSimpleName();
String tableTwoName = tableTwo.get(0).getClass().getSimpleName();
for (T1 t1 :
tableOne) {
Method method = t1.getClass().getMethod("getId");
Integer pkId = (Integer) method.invoke(t1);
List<T2> listForCurrentId = new ArrayList<>();
for (T2 t2 : tableTwo) {
Method fkMethod = t2.getClass().getDeclaredMethod("get".concat(tableOneName).concat("Id"));
Integer fkId = (Integer) fkMethod.invoke(t2);
if (pkId == fkId) {
listForCurrentId.add(t2);
}
}
Method tableTwoList = t1.getClass().getMethod("set".concat(tableTwoName).concat("List"), List.class);
tableTwoList.invoke(t1, listForCurrentId);
}
}
This is how I use it .
SystemDefaults systemDefaults = new SystemDefaults();
return Single.zip(systemDao.getRoles(), systemDao.getCountries(), systemDao.getCities(), (roles, countries, cities) -> {
systemDefaults.setRoles(roles);
*ConvenienceMethods.oneToMany(countries,cities);*
systemDefaults.setCountries(countries);
return systemDefaults;
});
GreenDao provides an addProtobufEntity method to let you persist protobuf objects directly. Unfortunately I can't find much documentation explaining how to use this feature.
Let's say I'm trying to add a foreign key into my Message entity so I can access its PBSender protobuf entity. Here's my generator code:
// Define the protobuf entity
Entity pbSender = schema.addProtobufEntity(PBSender.class.getSimpleName());
pbSender.addIdProperty().autoincrement();
// Set up a foreign key in the message entity to its pbSender
Property pbSenderFK = message.addLongProperty("pbSenderFK").getProperty();
message.addToOne(pbSender, pbSenderFK, "pbSender");
Unfortunately the generated code doesn't compile because it is trying to access a non-existant getId() method on my PBSender class:
public void setPbSender(PBSender pbSender) {
synchronized (this) {
this.pbSender = pbSender;
pbSenderID = pbSender == null ? null : pbSender.getId();
pbSender__resolvedKey = pbSenderID;
}
}
Can anybody explain how relationships to protocol buffer entities are supposed to be managed?
GreenDao currently only supports Long primary keys. Does my protobuf object need a method to return a unique Long ID for use as a primary key?
If I remove my autoincremented ID then the generation step fails with this error:
java.lang.RuntimeException: Currently only single FK columns are supported: ToOne 'pbSender' from Message to PBSender
The greenDAO generator Entity source code suggests it currently does not support relations to protocol buffer entities:
public ToMany addToMany(Property[] sourceProperties, Entity target, Property[] targetProperties) {
if (protobuf) {
throw new IllegalStateException("Protobuf entities do not support realtions, currently");
}
ToMany toMany = new ToMany(schema, this, sourceProperties, target, targetProperties);
toManyRelations.add(toMany);
target.incomingToManyRelations.add(toMany);
return toMany;
}
/**
* Adds a to-one relationship to the given target entity using the given given foreign key property (which belongs
* to this entity).
*/
public ToOne addToOne(Entity target, Property fkProperty) {
if (protobuf) {
throw new IllegalStateException("Protobuf entities do not support realtions, currently");
}
Property[] fkProperties = {fkProperty};
ToOne toOne = new ToOne(schema, this, target, fkProperties, true);
toOneRelations.add(toOne);
return toOne;
}
However, I suspect that you could make this work if your Protobuf class contains a unique long ID and a public Long getId() method to return that ID.
In the greendao FAQs it says "Starting from greenDAO there’s limited support for String primary keys." http://greendao-orm.com/documentation/technical-faq/
I can't find anywhere that says how to do this.
I am using Guids as my primary key in a server application, and want to be able to generate new data remotely from an android device and upload this back to the server. The database on the android device is in sqlite and uses greenDAO to generate POJOs and data access layer. I am using Guids to avoid primary key collisions when data is uploaded to the server. I am storing the Guids as strings.
There is some more advice on the greendao website that says I should create a secondary field holding the string and still use the long primary key favoured by greendao, but this means that I have to reconnect all my database relationships when I import data from the server to the app which is a pain. Would much rather just continue to use the string primary keys if that is possible.
Can anybody tell me how to do this?
Here is some example code...
In my generator (I've removed most of the fields for clarity):
private static void addTables(Schema schema)
{
Entity unit = addUnit(schema);
Entity forSale = addForSale(schema);
Property unitIntId = forSale.addLongProperty("unitIntId").getProperty();
forSale.addToOne(unit, unitIntId);
}
private static Entity addForSale(Schema schema)
{
Entity thisEntity = schema.addEntity("ForSale");
thisEntity.addIdProperty();
thisEntity.addStringProperty("forSaleId");
thisEntity.addFloatProperty("currentPriceSqFt");
thisEntity.addStringProperty("unitId");
return thisEntity;
}
private static Entity addUnit(Schema schema)
{
Entity thisEntity = schema.addEntity("Unit");
thisEntity.addIdProperty();
thisEntity.addStringProperty("unitId");
thisEntity.addStringProperty("name");
return thisEntity;
}
In my android application I download all the data from the server. It has relationships based on the GUID id's. I have to reattach these to the int Id's I created in the generator like this:
//Add relations based on GUID relations
//ForSale:Units
for(ForSale forSale:Globals.getInstance().forSales)
{
if (forSale.getUnitId() != null && forSale.getUnit() == null)
{
for(Unit unit:Globals.getInstance().units)
{
if (forSale.getUnitId().equals(unit.getUnitId()))
{
forSale.setUnit(unit);
break; //only need the first one
}
}
}
}
So I end up having two sets of Id's linking everything, the int one for greendao and the string (guid) one that will work when it gets uploaded back to the server. Must be an easier way!
Try this:
private static void addTables(Schema schema) {
Entity unit = addUnit(schema);
Entity forSale = addForSale(schema);
Property unitId = forSale.addStringProperty("unitId").getProperty();
forSale.addToOne(unit, unitId);
}
private static Entity addForSale(Schema schema) {
Entity thisEntity = schema.addEntity("ForSale");
thisEntity.addStringProperty("forSaleId").primaryKey();
thisEntity.addFloatProperty("currentPriceSqFt");
return thisEntity;
}
private static Entity addUnit(Schema schema) {
Entity thisEntity = schema.addEntity("Unit");
thisEntity.addStringProperty("unitId").primaryKey();
thisEntity.addStringProperty("name");
return thisEntity;
}
I don't know if the ToOne-Mapping will work with strings, though. If it doesn't you can add some methods for getting the related objects in the KEEP-SECTIONS.