Greendao: How to update to-many entities - android

My app uses a database with SongVersions having many Tracks. This to-manx relationship is generated by greendao and works fine so far.
But now I try to update a track - let's say to mute it - and the update is only wokring until the next app start. This is, because the udpate is only reflected in the cached list of tracks, but not really persisted to the database.
Here is my code so far (not working)
// iterate through tracks
for (Track track : mSongVersion.getTrackList()) {
if (trackId.equals(track.getId())) {
// mute specific track
track.setMuted(muted);
mSongVersion.update();
}
}
SongVersion and Track are the entitiy classes genertated by greendao. SongVersion has an update()-method, but Track has no update()-method. So I was thinking that one has to update a specific track by updating "the whole thing" using SongVersion#update().
But this call only updates the SongVersion, ignoring changes of its tracks...
I also tried some variations of (not) resetting the tracklist to make sure that (no) cached values are interfering, but also to no avail.
EDIT:
Here's some code that might help.
SongVersion (generated by greendao!):
/** Used for active entity operations. */
private transient SongVersionDao myDao;
/** called by internal mechanisms, do not call yourself. */
public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getSongVersionDao() : null;
}
public void update() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.update(this);
}
Parts of the Database generation java:
public static void main(String[] args) throws Exception {
Schema schema = new Schema(1, "my.app.database");
Entity songVersion = schema.addEntity("SongVersion");
Entity track = schema.addEntity("Track");
// SongVersion fields w/o relations
songVersion.setHasKeepSections(true);
songVersion.addIdProperty().autoincrement();
songVersion.addStringProperty("name").notNull();
// Track fields w/o relations
track.setHasKeepSections(true);
track.implementsInterface("Comparable");
track.addIdProperty().autoincrement();
track.addBooleanProperty("muted").notNull();
// relations
Property songVersionId =
track.addLongProperty("songVersionId").notNull().getProperty();
songVersion.addToMany(track, songVersionId);
}

I think that you should use the TrackDAO to update the track after modifying the list instance. Something like this:
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
trackDao = daoSession.getTrackDao();
// iterate through tracks
for (Track track : mSongVersion.getTrackList()) {
if (trackId.equals(track.getId())) {
// mute specific track
track.setMuted(muted);
trackDao.update(track);
}
}

Related

GreenDAO does not persist data across application restart

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.

Seed GreenDao database on Android

I'm using GreenDAO for an Android project for the first time, and was wondering how to seed the database for first-time users? Say for instance i have a table and want 5 rows inserted on behalf of the user.
Also, i might add new tables in future updates and seed data into those as well, but still want to have the five rows inserted into the first table, even though the user is installing a newer version of the scheme.
My initial idea was to do it in my App.onCreate() method, and then set a flag in SharedPreferences as whether or not the seed has been made already, but it bugs me that i can't find a more pragmatic approach to this.
Any help appreciated, thanks!
I had the same problem and searched the web and the documentation of GreenDAO but didn't find anything reliable.
So I wrote a code to run in the first run of the app. To do so I needed to check if it's the first time that my app is launched. For doing that I recommend this answer. You can see the code from that answer here:
public static void checkFirstRun(Context context) {
final String PREFS_NAME = "TickTockPrefs";
final String PREF_VERSION_CODE_KEY = "version_code";
final int DOESNT_EXIST = -1;
// Get current version code
int currentVersionCode = 0;
try {
currentVersionCode = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
} catch (android.content.pm.PackageManager.NameNotFoundException e) {
// handle exception
e.printStackTrace();
return;
}
// Get saved version code
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
int savedVersionCode = prefs.getInt(PREF_VERSION_CODE_KEY, DOESNT_EXIST);
// Check for first run or upgrade
if (currentVersionCode == savedVersionCode) {
// This is just a normal run
return;
} else if (savedVersionCode == DOESNT_EXIST) {
// TODO This is a new install (or the user cleared the shared preferences)
seed(context);
} else if (currentVersionCode > savedVersionCode) {
// TODO This is an upgrade
}
// Update the shared preferences with the current version code
prefs.edit().putInt(PREF_VERSION_CODE_KEY, currentVersionCode).apply();
}
And inside the seed method you can write whatever you want to insert. For example say I have a "Person" entity that I want to prepopulate with data:
public static void seed(Context context) {
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "your-db", null);
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
DaoSession daoSession = daoMaster.newSession();
Person person = new Person();
person.setName("Jason");
person.setFamily("Bourne");
PersonDao personDao = daoSession.getPersonDao();
personDao.insert(person);
}
Note that if you want to insert a List of entities use insertInTx() method instead of insert(). You can see the difference here.
I know this is different than ORM seed method but it seems there's no other alternatives except you manipulate greenDAO code yourself.

Save complex objects and their sons entities ORM Lite

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 string primary keys - how to use

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.

OrmLite: Advanced where logic

I have these tables in an Android based application where I'm using OrmLite for the database management.
What I want to have an x number of array list depending on how many of the product type FOLDER I have.
So in this case I want to a list of products where the productId equals parentId.
So I want a list where
if(productType = FOLDER) {
if(productId = parentId){
//add product
}
}
Basically what I want to end up with, in this case three lists with each containing a list of products where parentId is the same for every product.
I've tried many things, and some works better than others, but a code I want to run actually throws a nullpointer.
DatabaseHelper dbHelper = getHelper();
List<Product> productsParents = null;
try {
Dao<Product, Integer> dao = dbHelper.getDao();
PreparedQuery<Product> prepQu = dao.queryBuilder().where()
.eq("parentId", dao.queryBuilder().selectColumns("productId").where()
.eq("productType", ProductType.FOLDER).prepare()).prepare();
productsParents = dao.query(prepQu);
} catch (SQLException e) {
...
}
This code isn't working because productParents returns null, and it does not do what I want, even though it's a slight hint. If someone know how to do this in code that would be sufficient also, or more likely a mix of java and ormlite.
Have you had a chance to RTFM around building queries? The ORMLite docs are pretty extensive:
http://ormlite.com/docs/query-builder
Your problem is that a prepared query cannot be an argument to the eq(...) method. Not sure where you saw an example of that form.
So there are a couple ways you can do this. The easiest way is to do a different query for each productType:
Where<Product, Integer> where = dao.queryBuilder().where();
where.eq("parentId", parentId).and().eq("productType", ProductType.FOLDER);
productsParents = where.query();
// then do another similar query again with ProductType.PRODUCT, ...
If you want to do just one query then you can get all products that match the parentId and then separate them using code:
Where<Product, Integer> where = dao.queryBuilder().where();
where.eq("parentId", parentId);
productsParents = where.query();
List<Product> productFolders = new ArrayList<Product>();
List<Product> productProducts = new ArrayList<Product>();
...
for (Product product : productsParents) {
if (product.getProductType() == ProductType.FOLDER) {
productFolders.add(product);
} else if (product.getProductType() == ProductType.PRODUCT) {
productProducts.add(product);
} else ...
}

Categories

Resources