I created an entity which need to identity by Short only.
Here is my generated code:
public Source(Short id, String name) {
this.id = id;
this.name = name;
}
TestCode DatabaseHelperTest.java:
public void testInsertAndLoad(){
Source source = new Source((short) 0, "TestSource");
SourceDao sourceDao = daoSession.getSourceDao(); //#line 26
sourceDao.insert(source);
Short id = source.getId();
assertNotNull(id);
}
When I run test, I got the ClassCastException:
Running tests
Test running started
java.lang.ClassCastException: java.lang.Short cannot be cast to java.lang.Long
at de.greenrobot.dao.identityscope.IdentityScopeLong.put(IdentityScopeLong.java:31)
at de.greenrobot.dao.AbstractDao.attachEntity(AbstractDao.java:695)
at de.greenrobot.dao.AbstractDao.updateKeyAfterInsertAndAttach(AbstractDao.java:362)
at de.greenrobot.dao.AbstractDao.executeInsert(AbstractDao.java:355)
at de.greenrobot.dao.AbstractDao.insert(AbstractDao.java:293)
at com.tuanchau.DatabaseHelperTest.testInsertAndLoad(DatabaseHelperTest.java:26)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1584)
So, does GreenDAO allow to make short become primary key?
And, how can I deal with this exception.
Thanks
Update:
DB Generation code
Entity source = schema.addEntity("Source");
Entity category = schema.addEntity("Category");
source.addShortProperty("id").primaryKey().getProperty();
source.addStringProperty("name").notNull();
category.addIntegerProperty("id").primaryKey().getProperty();
category.addStringProperty("name").notNull();
Property csid = category.addLongProperty("sid").notNull().getProperty();
category.addToOne(source, csid);
Source Properties
public static class Properties {
public final static Property Id = new Property(0, Short.class, "id", true, "ID");
public final static Property Name = new Property(1, String.class, "name", false, "NAME");
};
Category Properties
public static class Properties {
public final static Property Id = new Property(0, Integer.class, "id", true, "ID");
public final static Property Name = new Property(1, String.class, "name", false, "NAME");
public final static Property Sid = new Property(2, short.class, "sid", false, "SID");
};
From greenDao website:
Current primary key (PK) restrictions: Currently, entities must have a long or Long property as their primary key. This is recommended practice for Android and SQLite. greenDAO is prepared to handle any primary key scenario in the future, but not everything is implemented completely yet. To work around this issue, you can use a long primary key and use an unique index for the intended “key” properties.
You could try using something like this:
source.addIdProperty();
source.addShortProperty("shortId").unique().getProperty();
source.addStringProperty("name").notNull();
Related
I'm trying to create entities in my database using GreenDAO. My object is defined by this class:
public class Container {
String id; //GUID
String name;
Product product;
}
public class Product {
String urlImage;
String productName;
String description;
}
The following code is defining my entities:
Entity container = schema.addEntity("Container");
Property productsForeignKey = container.addStringProperty("id").notNull().unique().getProperty();
container.addStringProperty("name");
Entity product = schema.addEntity("Product");
product.addStringProperty("productName");
product.addStringProperty("urlImage");
product.addStringProperty("description");
containerToProduct = container.addToOne(product, productsForeignKey);
In the end I get this exception:
java.lang.RuntimeException: Currently only single FK columns are supported: ToOne 'product' from Container to Product
What I'm doing wrong?
I solved it by using Container's primary key as Product's foreign key. It turns out that String Foreign Key is not a good choice. So, my solution is more like this:
Entity container = schema.addEntity("Container");
Property productsForeignKey = container.addLongProperty("newId").primaryKey().autoincrement().getProperty();
container.addStringProperty("name");
Entity product = schema.addEntity("Product");
product.addStringProperty("productName");
product.addStringProperty("urlImage");
product.addStringProperty("description");
containerToProduct = container.addToOne(product, productsForeignKey);
Where newId is just random field used for convenience.
I'm defining table/field names thru usual annotations, like:
#DatabaseTable(tableName = "M")
public class Headers {
//android requires primary key as _ID
#DatabaseField(generatedId = true, columnName = "_ID", dataType = DataType.INTEGER_OBJ)
private Integer id;
#DatabaseField(
columnName = "MM2",
uniqueCombo = true,
canBeNull = true, //null means root folder
dataType = DataType.INTEGER_OBJ
)
private Integer parentId;
}
Meantime somewhere deep in my code, I need to make some queries, like:
QueryBuilder<Headers, Integer> qb = headersDao.queryBuilder();
qb.where().eq("_ID", headerId); //_ID field name
This kind of code looks ugly, since field name (in this case _ID) is hardcoded (even if would be declared as final static String)
Question: is it normal OrmLite coding practice? I'd be expecting that I could use instead of hardcoded _ID field some small code. For sure OrmLite "knows" all field names. Anyone can help me?
Define a constant for the field name, like
private static final String FIELD_ID = "_ID";
// Or make it public as Gray said into comment
public static final String FIELD_ID = "_ID";
and use it as like
//android requires primary key as _ID
#DatabaseField(generatedId = true, columnName = FIELD_ID, dataType = DataType.INTEGER_OBJ)
private Integer id;
And into your query
QueryBuilder<Headers, Integer> qb = headersDao.queryBuilder();
qb.where().eq(FIELD_ID, headerId); //_ID field name
If you look at documentation
They also follow the same
QueryBuilder<Account, String> qb = accountDao.queryBuilder();
Where where = qb.where();
// the name field must be equal to "foo"
where.eq(Account.NAME_FIELD_NAME, "foo");
// and
where.and();
// the password field must be equal to "_secret"
where.eq(Account.PASSWORD_FIELD_NAME, "_secret");
PreparedQuery<Account, String> preparedQuery = qb.prepareQuery();
I have a problem with a foreign key in ORMLite I have 2 classes QuestionDb and ResponsesDb which are the following :
public class ResponsesDb {
public static final String FIELD_ID = "id";
#DatabaseField(generatedId = true,columnName=FIELD_ID)
private int id;
#DatabaseField(canBeNull = false, foreign = true, foreignColumnName=QuestionDb.FIELD_REF)
private QuestionDb question;
#DatabaseField(canBeNull = false)
private String answer;
}
And :
#DatabaseTable(tableName = "question")
public class QuestionDb implements Serializable {
public static final String FIELD_REF = "ref";
private static final long serialVersionUID = 4106020204304605623L;
#DatabaseField(generatedId = true)
private int id;
#DatabaseField(canBeNull = false, unique = true, columnName=FIELD_REF, index=true)
private String ref;
#ForeignCollectionField(foreignFieldName = "question", eager = true)
private ForeignCollection<ResponsesDb> responses;
}
My problem is when i do that :
QueryBuilder<QuestionDb, Integer> questionQuery = helper
.getQuestionDao().queryBuilder();
QueryBuilder<ResponsesDb, Integer> responseQuery = helper
.getResponseDao().queryBuilder();
responseQuery = responseQuery.join(questionQuery);
I recieve that :
05-27 12:00:01.577: W/System.err(7272): java.sql.SQLException: Could not find a foreign class model.ormlite.tableClass.ResponsesDb field in class model.ormlite.tableClass.QuestionDb or vice versa
But if I remove the field foreignColumnName=QuestionDb.FIELD_REF from question field's annotation in ResponsesDb, the query works.
The fact is that as my program update the database, the id field can change so I want that the foreign key is ref. Do you have any idea how I can fix this problem ?
You can use a string as a foreign key. What you cannot do is define a foreign key with a foreignColumnName that is not the key of the other object.
From the example in the ORMLite documentation:
With foreign objects, just the id field from the Account is persisted
to the Order table as the column "account_id".
In this case, you have in QuestionDb:
#DatabaseField(generatedId = true)
private int id;
The generatedId annotation means that this is the id of the table. Having marked ref as unique will create a unique index, but it does not make it a candidate key. Thus it cannot be used as a foreign key from another table.
In short: if you need a string foreign key, then you can. Just define a string primary key in the referenced table. (i.e. remove the id field and put #DatabaseField(id = true) in the ref field.
Going further back, howerver, I don't understand why you claim:
The fact is that as my program update the database, the id field can
change
The generatedId value will not change for a row after inserting it. It's perfect for use as a foreign key! :)
I'm new to Android and I have problem with ORMLITE.
For example let's say I have this table:
#DatabaseTable(tableName = "accounts")
public class Account {
#DatabaseField(id = true)
private int id;
#DatabaseField(canBeNull = false)
private String name;
…
and I want to add new data into my table without setting id.
I tried this way:
#DatabaseTable(tableName = "accounts")
public class Account {
#DatabaseField(generatedId = true,allowGeneratedIdInsert=true)
private int id;
#DatabaseField(canBeNull = false)
private String name;
…
> Account acc = new Account();
>
> acc.setName("Example");
>
> AccountDao.createOrupdate(acc);
Here I can't insert acc into my database because acc id is zero. I want to generate id. Can I use autoincrement?
Here i can't insert acc into my database because acc id is zero. I want to generate id. Can i use autoincrement ?
So to quote the javadocs for the allowGeneratedIdInsert field in #DatabaseField:
If this is set to true then inserting an object with the ID field already set will not override it with a generated-id. This is useful when you have a table where items sometimes have IDs and sometimes need them generated. This only works if the database supports this behavior and if generatedId() is also true for the field.
So if you have acc.id set to a non-0 value, it should be inserted into the database with the id from acc. If you want acc to get an auto-generated id then you should just set acc.id to be 0.
For an example, you could take a look at the ORMLite Android test class. Search for the testCreateWithAllowGeneratedIdInsert() method which has code like:
AllowGeneratedIdInsert foo = new AllowGeneratedIdInsert();
assertEquals(1, dao.create(foo));
AllowGeneratedIdInsert result = dao.queryForId(foo.id);
assertEquals(foo.id, result.id);
...
AllowGeneratedIdInsert foo3 = new AllowGeneratedIdInsert();
foo3.id = 10002;
assertEquals(1, dao.create(foo3));
result = dao.queryForId(foo3.id);
assertEquals(foo3.id, result.id);
NOTE: the docs mention that this only works if the database supports it but Sqlite is one of those databases.
Use Wrapper class instead of int
#DatabaseField(id = true)
private Integer id;
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.