1

I'm targeting Android 2.2 and newer. This error was generated on a device running 4.x. I am using ORMLite 4.38 libraries.

I need to guarantee every record instance is unique for any number of devices. I was happy to see that ORMLite supports UUIDs as IDs. I've created a UUID - id abstract base class for my database record definitions. allowGeneratedIdInsert is the perfect solution. But this feature seems to cause an 'IllegalStateException: could not create data element in dao'. I tested by removing this annotation, and no issue. Put it back in...same issue. Put the base class stuff in one record definition...same issue.

LogCat also reports:

Caused by: java.sql.SQLException: Unable to run insert stmt on object - objectid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx

public abstract class UUIDDaoEnabled<T> extends BaseDaoEnabled<T, UUID> {

    //allowGeneratedIdInsert allows us to set UUID when this device db didn't create it
    @DatabaseField(generatedId = true, allowGeneratedIdInsert=true)
    private UUID id;
    ...
    public void setUUIDFromSerializedSource(SerializedModelBinaryInputStream stream, Dao<T, UUID> dao) throws SQLException { //only place we can set UUIDs
        if(id == null)
            dao.refresh((T)this); 

        if(id != null)
            throw new SQLException("Trying to set UUID on existing object");

        id = stream.getCurrentUUID();
    }
    }

I'll specialize like so:

@DatabaseTable()
public class Type extends UUIDDaoEnabled<Type> { ... }

I can't explain this from the documentation for allowGeneratedIdInsert and generatedId. In fact the documentation for alloeGeneratedIdInsert says it overrides the default behavior of generatedId. It also says

This only works if the database supports this behavior

Yet, I have read in other posts that ORMLite 4.25 (?) and newer supports this behavior on Android devices. So, either that's not entirely true. Or I'm doing something stupid...anyone???

UPDATE: after thinking about it for a minute, I realized that neither allowGeneratedIdInsert support, nor inheritance can be the root cause, because I instantiate other objects based on the same abstract class. What I can't figure out is why one particular class is causing the issue. The only unique thing about the offending record type (compared to other types that create) is it is a many in a one to many, and it contains several to manies. Could these properties, combined with allowGenereatedIdInsert, be the root issue? Rather, I should ask, has anyone seen this issue in this circumstance?

UPDATE: nevermind the question. I can use updateId(...) instead of allowGeneratedIdInsert.

Ria
  • 10,237
  • 3
  • 33
  • 60
stephen
  • 1,039
  • 14
  • 31
  • Sounds like you have worked around the problem but I'm still interested in the cause. Can you post the _full_ exception stack trace somewhere? Maybe http://pastebin.com/ – Gray Feb 02 '13 at 14:56
  • @Gray A month should give you enough time: http://pastebin.com/Ebv77KHw – stephen Feb 02 '13 at 18:04
  • @Gray A <->> B <<-> C, A <->> D <<->C. The exception occurs when A, B, C, and D exist, and I add the first reference in B's foreign collection to C. – stephen Feb 02 '13 at 18:18
  • @Gray I confuse myself when I speak so abstractly. It's when I add the first reference to B in C's foreign collection to B records...not the other way around. – stephen Feb 02 '13 at 19:29

2 Answers2

3

So I'm not sure about this but it looks to me that you are trying to insert an element twice into a table with the same UUID id. The exception is saying there is a constraints failure:

IllegalStateException: Could not create data element in dao
    at BaseForeignCollection.add(BaseForeignCollection.java:57)
...
Caused by: SQLiteConstraintException: error code 19: constraint failed

If you call foreignCollection.add(...); it does the same thing as dao.create(...); -- and you can't do both of these with the same object. If you have an existing object that has already been created by the DAO and you want to associate it with another object, you should do something like:

   // associate this object with another
   existingObject.setForeignField(...);
   // now update it in the db
   existingObjectDao.update(existingObject);

You can't add it to the foreignField's foreign collection.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • Thanks. I had just discovered that I was creating multiple instances, and after a few log dumps realized that by setting the to-one relationship the inverse to-many is set automagically. Applied to your suggestion, I created B, set the B->C to-one, then the C->B to-many. Now knowing this, the issue pertaining to allowGeneratedIdInsert goes away. Related note: I hate to get dumped into the RTFM manual group. I did look. I couldn't find anything that suggested this behavior in the documentation regarding foreign relationships. Am I missing something? – stephen Feb 02 '13 at 20:48
  • I've added something like the following copy to the javadocs and the manual: NOTE: When you are adding a new object to a foreign collection, this will also add it to the database by calling through to dao.create(obj). If the object has already been created in the database then you should instead set the foreign field on the object and call dao.update(obj). If you add it here the DAO will try to create it in the database again which will most likely cause an error. – Gray Feb 02 '13 at 21:05
  • You might add the same note here as well: http://ormlite.com/javadoc/ormlite-core/com/j256/ormlite/field/DatabaseField.html#foreign() – stephen Feb 02 '13 at 21:06
  • It's still bizarre that the exception only occurred when using allowGeneratedIdInsert for the UUID id. To clarify your note, you might annotate "error" with "logical error". – stephen Feb 02 '13 at 21:08
  • One more clarification: If I set the to-one relationship to null and update, is the inverse relationship removed from the ForeignCollection? You might clarify this in your note. – stephen Feb 02 '13 at 22:35
1

I had a similar problem. But it was caused by using create instead createOrUpdate to save the object.

It is also important to uninstall the application before changing this to ensure that the database has been removed and will not keep the old behavior.

Edit: createOrUpdate is very time expensive. It's better use just create with great amounts of data.

Edit 2:It is also bether to use a TransactionManager.callInTransaction.

Community
  • 1
  • 1
Natanael
  • 2,290
  • 6
  • 23
  • 35