I'm pretty new to Android development in general, and I've never even used greenDAO. But after spending a lot of time working on my generator class (Where I model my entities), I was finally able to produce something that looked similar to the example given on GitHub.
import de.greenrobot.daogenerator.DaoGenerator;
import de.greenrobot.daogenerator.Entity;
import de.greenrobot.daogenerator.Property;
import de.greenrobot.daogenerator.Schema;
import de.greenrobot.daogenerator.ToMany;
public class simbalDAOgen {
public static void main(String[] args) throws Exception {
Schema schema = new Schema(1, "com.bkp.simbal"); //Schema(Int version, String package name)
addCBTrans(schema); //Add the entities to the schema
new DaoGenerator().generateAll(schema, "../Simbal/src-gen", "../Simbal/src-test"); //Generate DAO files
}
private static void addCBTrans(Schema schema){
Entity checkbook = schema.addEntity("Checkbook");
checkbook.addIdProperty();
checkbook.addStringProperty("name").notNull();
checkbook.addDateProperty("dateModified");
checkbook.addStringProperty("balance"); // Use a string property because BigDecimal type should be used for currency
Entity transaction = schema.addEntity("Transaction");
transaction.setTableName("TRANS"); // "TRANSACTION" is a reserved SQLite keyword
transaction.addIdProperty();
transaction.addStringProperty("name");
transaction.addStringProperty("category");
Property transDate = transaction.addDateProperty("date").getProperty();
transaction.addStringProperty("amount"); // Again use string for BigDecimal type
transaction.addStringProperty("notes");
Property cbName = transaction.addStringProperty("cb").notNull().getProperty(); //What checkbook the transaction is in
ToMany cbToTrans = checkbook.addToMany(transaction, cbName); //Actually ties the transactions to their correct checkbooks
cbToTrans.setName("Transactions");
cbToTrans.orderAsc(transDate);
}
}
I then ran the code as a java application to generate my DAO files, just like the documentation on greenDAO says to. The files were successfully generated, however I did get this line in the console in Eclipse:
Warning to-one property type does not match target key type: ToMany 'Transactions' from Checkbook to Transaction
I'm really not sure if I need to be concerned since the files were generated. But what I don't understand is why there's mention of a "to-one" relation when I'm using a "to-many" relation, as can be seen in my code. (There can be many transaction entities in a checkbook entity, and I'm intending to use each checkbook entitys' name to tie the transactions to it.)
Do I need to go back and fix a part of my code? Please ask if I need to clarify anything, and thanks for your time!
After looking at the files generated for me by greenDAO, I've found the solution to my problem. It seems to me that the addToMany() method expects a Long property to be passed to it and I was using a String property. So I changed these two lines in my generator code:
Property cbName = transaction.addStringProperty("cb").notNull().getProperty();
ToMany cbToTrans = checkbook.addToMany(transaction, cbName);
to this:
Property checkbookId = transaction.addLongProperty("checkbookId").notNull().getProperty();
ToMany cbToTrans = checkbook.addToMany(transaction, checkbookId);
which solved my problem. I was under the impression I could use any type of property to tie the transactions to the checkbook, so I was trying the use the checkbook name.
it seems to be GreenDao only accepts type Long as foreign key
Related
We had to migrate one of the field-params in an entity from long to BigDecimal. Migration is quite smooth but there is a problem; we want to keep previous values to be set to the migrated field. But as soon as ObjectBox is initialized it defaults migrated field to the default value of the current type, in our case, to null.
Say we had:
Id (long)
Name
123
Random Name
After migration we got:
Id (String)
Name
null
Random Name
Is there any possible way to migrate without losing values on migrated fields?
A side note: I have used a converter to keep the BigDecimal values since ObjectBox doesn't support BigDecimal
Converter class:
public class BigIntegerStringConverter implements PropertyConverter<BigInteger, String> {
#Override
public BigInteger convertToEntityProperty(String databaseValue) {
return databaseValue == null ? null : new BigInteger(databaseValue);
}
#Override
public String convertToDatabaseValue(BigInteger entityProperty) {
return String.valueOf(entityProperty);
}
}
Usage:
#Convert(converter = BigIntegerStringConverter.class, dbType = String.class)
#Uid(XXXXXXXX)
BigInteger tigerId;
ObjectBox does not support migrating existing property data to a new type. You will have to take care of this yourself, e.g. by keeping the old property and adding some migration logic.
Source: https://docs.objectbox.io/advanced/data-model-updates#changing-property-types
#Farid a manual migration could look somewhat like this:
add a new field to the model, with the new type you want to use, e.g. newField
add code that updates all objects, reading oldField and writing the appropriate value to newField
remove oldField from the model, now that all the data is migrated
optionally, you can follow the docs on how to rename newField to anything you want
Unfortunately, type migration where old data is kept is not supported in ObjectBox.
Reference: https://github.com/objectbox/objectbox-java/issues/971
So, I have the very simple problem, that I cannot seem to format a double coming from an ObservableField in a data binding. I have the following layout:
android:text='#{String.format("%.2f€", transaction.value)}'
and here the definition of transaction.value:
public final ObservableField<Double> value = new ObservableField<>();
I always get this error:
java.util.IllegalFormatConversionException: f != android.databinding.ObservableField
I get the same issue if I use an ObservableDouble. The only way to avoid this seems to be if I call transaction.value.get() in the binding, but I was under the impression that get/set can be omitted here, as I am successfully doing e.g. with an ObservableField<Date>.
I am targeting Sdk 26 with with buildTools 26.0.2.
Update 1
I tried the same thing now with my previous setup, and it worked, just as I remembered. So I pinpointed it to the Gradle version change from 2.3.1 to 3.0.1 (even when I put in target/compile Sdk 26 and buildToolsVersion 26.0.2 with Gradle 2.3.1 it works).
As #dominicoder pointed out to look into the generated data bindings, here is the difference, explaining the problem:
Gradle 2.3.1:
android.databinding.ObservableField<java.lang.Double>transactionValue=null;
java.lang.Double transactionValueGet=null;
....
if(transaction!=null){
// read transaction.value
transactionValue=transaction.value;
}
updateRegistration(3,transactionValue);
if(transactionValue!=null){
// read transaction.value.get()
transactionValueGet=transactionValue.get();
}
// read String.format("%.2f€", transaction.value.get())
stringFormatJavaLangString2fTransactionValue=java.lang.String.format("%.2f€",transactionValueGet);
Gradle 3.0.1:
android.databinding.ObservableField<java.lang.Double> transactionValue = null;
// NO transactionValueGet field !!!
....
if(transaction!=null){
// read transaction.value
transactionValue=transaction.value;
}
updateRegistration(3,transactionValue);
// read String.format("%.2f€", transaction.value)
stringFormatJavaLangString2fTransactionValue=java.lang.String.format("%.2f€",transactionValue);
So - it looks like this is a bug introduced in that version update (to be safe, I created a completely new project with a similar setting, a TextView having its text bound to an ObservableField<Double> using String.format()). Or maybe it is intended behavior, but I really wouldn't understand the purpose of that kind ob breaking change.
Update 2
Currently the problem is filed in the Android issue tracker
The Problem
Check the databinding generated java file for your layout. You will find something like this:
android.databinding.ObservableField<java.lang.Double> transactionValue = null;
// read String.format("%.2f€", accountViewModel.test)
stringFormatJavaLangString2fAccountViewModelTest = java.lang.String.format("%.2f€", transactionValue);
You can see that it's using the observable field literally and I think it's because the signature of String.format is (String string, Object args...). Because the observable field is an Object it can be used as is, so the code generator does not try to "get" the double value instead.
The Date formatting example works because the format method has a signature of (Date date). Thus, the code generator realizes that the signature doesn't match, but the observable type does, so it "get"s it for you and you'll see something like this:
java.util.Date transactionDateGet = null;
// read transaction.date.get()
transactionDateGet = transactionDate.get();
androidTextFormatDateFormatGetDateFormatContextFormatTransactionDate =
android.text.format.DateFormat.getDateFormat(
getRoot().getContext()).format(transactionDateGet);
The Solution
As you already indicated, the easiest thing to do is just manually call .get() in your binding. However, I'd recommend avoiding doing logic in your XML, as #elmorabea indicated. Instead, I'd suggest using a view model that encapsulates the display logic:
public class TransactionViewModel {
private final Transaction mTransaction;
public TransactionViewModel(#NonNull Transaction transaction) {
mTransaction = transaction;
}
#Bindable
public String getTransactionValueText() {
return String.format("%.2f€", transaction.getValue());
}
}
Then you have something like this:
android:text='#{viewModel.transactionValueText}'
This is more work as you have to have an extra class for each layout file; you'd have to move your observable fields from your models to the viewmodels; and you'd have to make your view model obvserve the model to propagate the changes up the to view, but the separation of concerns makes it far easier to update, extend, reuse, and maintain in the long run.
Hope that helps!
I'm currently into evaluating GreenDAO for my application. I'm facing the following problem.
My app consists of several modules (seperated in packages, e.g. "com.example.app.results", "com.example.app.synchronization"). Some of them have no dependencies, some of them have dependencies on other modules (e.g. synchronization has a dependency on results, whereas results has no dependency).
What I would like to model is the following:
Module results has Entity MyResult (attributes: name, value).
Module synchronization has Entity MyResultSynchronization (attributes: MyResult (reference), date).
final Schema schema = new Schema(1, "com.example.app");
final Entity myresult = schema.addEntity("results.MyResult");
final Property myresultId = myresult.addIdProperty().getProperty();
myresult.addStringProperty("name");
myresult.addStringProperty("value");
final Entity myResultSynchronization = schema.addEntity("synchronization.MyResultSynchronization");
myResultSynchronization.addIdProperty();
myResultSynchronization.addDateProperty("date");
myResultSynchronization.addToOne(myresult, myresultId);
but - $entityPackage.$name does not what I expected it to do (neither did $package\$name ;-)).
My question is: Am I forced to have all entities of my app in a single package? Is what I'm trying to do feasible by creating multiple Schemas - but than again, is it possible to use the relate-feature between two (or more) schemas? What is the "right" way to do it? (Is there one?)
Indeed all entities have to be in the same package.
Normally you use a structure like
com.example.myapp.data
Where you put everything for managing your database, especially your entity classes. Inside you can let greendao create a dao package where it will put everything needed to access your data (base).
Of course you can enforce your naning schema by making multiple sxhemas in greendao. But the schemas will be independent: They won't use the same database and you won't be able to link them together with toOne () for example.
If you still want to use your naming schema you can generate everything to an intermediate package and move them to your desired packages manually. But you would have to repeat this upon every change to your database schema, which is more often than one may think at first.
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.
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.
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.
I've been trying to create a database where all the tables inherit a certain element in order to have to possibility to have meta-data.
there for I added in the model generator in all the table declarations this line:
public Entity addSuperEntity(Schema schema) {
Entity superEntity = schema.addEntity("superEntity");
superEntity.addIdProperty().primaryKey();
// SET RELATIONSHIP 1:m TO META DATA
}
public Entity addTable(Schema schema) {
Entity mEntity = schema.addEntity("MyEntity");
mEntity.setSuper("superEntity");
mEntity.addIdProperty().PrimaryKey();
// REST OF FIELDS
}
the question is:
now after I generated this to my Android project, how can I make sure that this still happens in real life? do I need to change anything now?
the official documentation doesn't have anything about inheritance.
Inheritance is supported for non-entity super classes using setSuperclass(String). An alternative is implementing interfaces using implementsInterface(String).
I updated the official docs with some details in the new section on inheritance and interfaces:
http://greendao-orm.com/documentation/modelling-entities/