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.
Related
I want to use GreenDAO for persistence, but I cannot get it to persist my data.
The data is saved and loaded correctly as long as the application is not restarted.
Once i swipe the app away and reopen it from scratch, GreenDAO does not see the previous data (both on the emulator and real device).
This is my entity:
#Entity
public class TestSingleEntity {
#Id(autoincrement = true)
Long id;
int someNumber;
public TestSingleEntity(int someNumber) {
this.someNumber = someNumber;
}
#Generated(hash = 787203968)
public TestSingleEntity(Long id, int someNumber) {
this.id = id;
this.someNumber = someNumber;
}
#Generated(hash = 1371368161)
public TestSingleEntity() {
}
// ... some more stuff
}
This is how I insert entities to database:
Random rnd = new Random();
TestSingleEntity singleEntity = new TestSingleEntity();
singleEntity.setSomeNumber(rnd.nextInt());
DaoSession session = ((MyApp)getApplication()).getDaoSession();
TestSingleEntityDao dao = session.getTestSingleEntityDao();
dao.insert(singleEntity);
Log.d("tgd", "Inserted an entity with id " + singleEntity.getId());
And this is how I read them:
Query query = dao.queryBuilder().orderAsc(TestSingleEntityDao.Properties.SomeNumber).build();
StringBuilder builder = new StringBuilder();
List<TestSingleEntity> result = query.list();
Log.d("size", result.size());
for (TestSingleEntity testSingleEntity : result) {
Log.d("entity", testSingleEntity.toString());
}
As I have said, as long as I stay in the app (moving around in different activities is okay), everytime the insert is called, a new entity with a new ID is created. As soon as I relaunch the app, it goes back to square one.
The setup was taken directly from the GitHub page. What am I doing wrong? Thanks
Disclaimer: GreenDAO has gone through major changes since I last used it so this is purely based on reading their code on the github.
Apparently GreenDAO's poorly documented DevOpenHelper drops all tables on upgrade, so the real question is why is onUpgrade being called when clearly there hasn't been a change to the schema version. Try to look for the log line that mentions dropping the tables as described in the template for DevOpenHelper.
Regardless, using OpenHelper instead should fix the issue.
I have a problem where I can't retrieve entities using their keys. I can view the entities using datastore viewer so they do exist.
I tried the following:
Retrieving all entities using EntityManager.createQuery(), it works but I don't want to retrieve all of them.
I tried using key.getId() and KeyFactor.keyToString(key) with EntityManager.find() still returns null.
keys are stored in another entity, I am sure they are stored correctly since I can view them and compare them in the datastore viewer.
Part of my code:
key definition
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
...
}
Function to get the entity, auto generated by google plug-in(I changed it from Long id to key to try to solve the problem):
public Group getGroup(Key key) {
EntityManager mgr = getEntityManager();
Group group = null;
try {
group = mgr.find(Group.class, key);
} finally {
mgr.close();
}
return group;
}
I'm using ORM Lite on a project , I decided to use the facility to make the persistence part of the Web service once and can reuse it on Android.
But I am suffering a lot because possou complex objects that have multiple ForeignCollectionField and Foreign object , and at the hour of perssistir temenda these data is a headache, because I have to enter one by one of their children , I think the idea of an ORM is make life easier , ie you have to persist the object and father and all the rest is done behind the scenes ...
Well, it is now too late to give up lite ORM , I wonder if there is a way to do what sitei above ..
I found a piece of code here
tried to implement but it seems not work , just keeps saving the parent object .
follows the function I'm trying to use , but do not know whether imports are correct because the code I found in the link above did not have this data
public int create(Object entity, Context context) throws IllegalArgumentException, IllegalAccessException, SQLException, SQLException {
try{
if (entity!=null){
// Class type of entity used for reflection
Class clazz = entity.getClass();
// Search declared fields and save child entities before saving parent.
for(Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
// Inspect annotations
Annotation[] annotations = field.getDeclaredAnnotations();
try{
for(Annotation annotation : annotations) {
// Only consider fields with the DatabaseField annotation
if(annotation instanceof DatabaseField) {
// Check for foreign attribute
DatabaseField databaseField = (DatabaseField)annotation;
if(databaseField.foreign()) {
// Check for instance of Entity
Object object = field.get(entity);
Dao gDao = getDatabase(context).getDao(object.getClass());
gDao.create(object);
}
}else if (annotation instanceof ForeignCollectionField){
Object object = field.get(entity);
for(Object obj : new ArrayList<Object>((Collection<?>)object)){
Class c = obj.getClass();
Dao gDao = getDatabase(context).getDao(obj.getClass());
gDao.create(obj);
}
}
}
}catch (NullPointerException e){
e.printStackTrace();
}
}
// Retrieve the common DAO for the entity class
Dao dao = getDatabase(context).getDao(entity.getClass());
// Persist the entity to the database
return dao.create(entity);
}else
return 0;
}finally {
if (database != null) {
OpenHelperManager.releaseHelper();
database = null;
}
}
}
Leveraging the same post, also need a colução to delete cascade, imagine a situation where I have the following tables:
Company > Category> person> contact> Phone and email
Deleting and now I do as described in the documentation:
public int deleteCascade(Prefeitura prefeitura, Context context){
try{
Dao<Prefeitura, Integer> dao = getDatabase(context).getDao(Prefeitura.class);
DeleteBuilder db = dao.deleteBuilder();
db.where().eq("prefeitura_id", prefeitura.getId());
dao.delete(db.prepare());
// then call the super to delete the city
return dao.delete(prefeitura);
}catch (SQLException e){
e.printStackTrace();
}
return 0;
}
But the objects that are not directly linked the company would still be in the database, how could I do?
But without hacks, I want a clean code ...
I know ORM Lite really is lite, but one that saves the children create and delete cascade is essential for any ORM, hopefully for the next versions it is implemented, it is regrettable not have these features, for simple projects is very good, but in a complex project because a lot of headaches, I'm feeling on the skin.
Any help is welcome!
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.
I've been trying to create a DB model using GreenDAO. the problem started when I tried to create more than one relationship between a different tables.
basically, I have a Message table, a Conversation table and a User table.
the User has a list of messages, and the message has a parent conversation.
I tried writing this code for creating the DB:
private static void addUser(Schema schema) {
user = schema.addEntity("User");
userId = user.addIdProperty().getProperty();
user.addStringProperty("facebookId").unique().index();
user.addStringProperty("firstName");
user.addStringProperty("lastName");
user.addStringProperty("fullName");
user.addStringProperty("photoUrl");
}
private static void addMessage(Schema schema) {
message = schema.addEntity("Message");
messageId = message.addIdProperty().getProperty();
message.addStringProperty("messageId").primaryKey();
message.addDateProperty("date");
message.addStringProperty("content");
message.addStringProperty("typeString");
}
private static void addConversation(Schema schema) {
conversation = schema.addEntity("Conversation");
conversation.addIdProperty();
conversation.addStringProperty("conversationId");
// REST OF THE CODE
}
private static void fakeRelationship(Schema schema) {
Property author = message.addLongProperty("author").getProperty();
Property parent = message.addLongProperty("parent").getProperty();
message.addToOne(user, author);
message.addToOne(conversation, parent);
user.addToMany(message, author);
conversation.addToMany(message, parent);
}
after running this code, I got this error:
Exception in thread "main" java.lang.RuntimeException: Currently only single FK columns are supported: ToOne 'parent' from Message to Conversation
at de.greenrobot.daogenerator.ToOne.init3ndPass(ToOne.java:91)
at de.greenrobot.daogenerator.Entity.init3rdPassRelations(Entity.java:557)
at de.greenrobot.daogenerator.Entity.init3ndPass(Entity.java:550)
at de.greenrobot.daogenerator.Schema.init3ndPass(Schema.java:185)
at de.greenrobot.daogenerator.DaoGenerator.generateAll(DaoGenerator.java:94)
at de.greenrobot.daogenerator.DaoGenerator.generateAll(DaoGenerator.java:79)
at com.glidetalk.dao.generator.GlideDaoGenerator.main(GlideDaoGenerator.java:27)
does this actually meen that I can't create more than one relation for each table in my DB?!
do I have to write everything manually?
What you try to do is supported by greenDAO and your code looks good, too. I copied it into my workspace and it executed perfectly fine. So I guess something is wrong in the code you left out.