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.
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.
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);
}
}
I use GreenDao in my project and it does a good job for mapping my server model to the android device. What I have been struggling with for a while now is that dao.update and/or dao.updateInTx methods are not updating any rows in the database.
What I do:
/**
* The first approach -> All in one runnable
*
*/
// list definitions
ArrayList<Country> countryList;
ArrayList<Country> countryListDetail;
final DaoSession daoSession = daoMaster.newSession();
// execute everything in runnable
// in order to optimize insert time
daoSession.runInTx(new Runnable() {
#Override
public void run() {
CountryDao countryDao = new CaountryDao();
// delete all records first
countryDao.deleteAll();
// insert countries with less data
size = countryList.size();
for (int i=0; i < size; i++) {
countryDao.insert(countryList.get(i));
}
// update countries with more data
// ID's of the Objects in countryListDetail match those from
// the countryList, so the primary key mathces
size = countryListDetail.size();
for (int i=0; i < size; i++) {
countryDao.update(countryListDetail.get(i));
}
}
}
/**
* The second approach -> updateInTx()
*
*/
// list definitions
ArrayList<Country> countryList;
ArrayList<Country> countryListDetail;
// insert & update logic
final DaoSession daoSession = daoMaster.newSession();
CountryDao countryDao = new CaountryDao();
countryDao.insertInTx(countryList);
countryDao.updateInTx(countryListDetail);
In both cases, when I pull the database from the device and inspect it, the Country table has only the base insert data, but no detail data which should come from the update statements. When debugging, the GreenDao logic seems to execute the updateInsideSynchronized() method and stmt.execute() is also called. Does anybody know what I may be doing wrong?
Please enable this and check
dao.queryBuilder().LOG_VALUES=true;
dao.queryBuilder().LOG_SQL=true;
and make sure countryList and countryListDetail have the same Primarykey when you update
Probably your countyListDetail doesn't contain Country-objects, that have been queried using greendao. Hence the objects have "no idea about" the database, because there is no reference to the DaoSession. The update only works for objects already inserted or queried from the database.
If the data in countryList is included in the data of countryListDetail, I'd use insertOrReplace.
Otherwise you have to merge your Country-objects before insert or before update.
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 ...
}
I am trying to insert around 2800 records into the sqlite database, it is taking 150 sec, which is way too much! Could anyone please tell how to optimize this insertion.
public void createVariantEntry(ArrayList<ArrayList<String>> str) {
InsertHelper ih = new InsertHelper(Database, VARIANT_TABLE_NAME);
final int varid = ih.getColumnIndex(VARIANT_ID);
final int varmakeid = ih.getColumnIndex(VARIANT_MAKE_ID);
final int varmodid = ih.getColumnIndex(VARIANT_MODEL_ID);
final int varname = ih.getColumnIndex(VARIANT_NAME);
final int varposteddate = ih.getColumnIndex(VARIANT_POSTED_DATE);
for(int i=0;i<1253;i++)
{
ih.prepareForInsert();
ih.bind(varid, str.get(i).get(0));
ih.bind(varmakeid, str.get(i).get(1));
ih.bind(varmodid, str.get(i).get(2));
ih.bind(varname, str.get(i).get(3));
ih.bind(varposteddate, str.get(i).get(4));
ih.execute();
}
for(int i=1255;i<str.size();i++)
{
ih.prepareForInsert();
ih.bind(varid, str.get(i).get(0));
ih.bind(varmakeid, str.get(i).get(1));
ih.bind(varmodid, str.get(i).get(2));
ih.bind(varname, str.get(i).get(3));
ih.bind(varposteddate, str.get(i).get(4));
ih.execute();
}
ih.close();
}
a great boost in performance will be gained when using transactions.
try {
SQLiteDatabase db = MySQLiteOpenHelper.getWritableDatabse();
ContentValues values = new ContentValues();
db.beginTransaction();
while ( more_data_to_insert ) {
// put the data in 'values'
values.put("col_1", data_1);
values.put("col_2", data_2);
// ...
values.put("col_n", data_n);
// Insert the row into the database.
db.insert("table_name", null, values);
}
db.setTransactionSuccessful();
} catch ( SQLiteException e ) {
// handle your sqlite errors
} finally {
db.endTransaction();
}
and don't use InsertHelper. its deprecated now.
Here are some general tips that might help you:
You can bulkInsert or applyBatch using ContentProviders to do a bunch of operations in one go:
How to use bulkInsert() function in android?
You can use transactions to speed things up as well:
Android Database Transaction
In some cases DatabaseUtils.InsertHelper has been known to provide faster inserts than the normal sqlite insert:
http://www.outofwhatbox.com/blog/2010/12/android-using-databaseutils-inserthelper-for-faster-insertions-into-sqlite-database/
After this, You'll have to do some benchmarking and optimize for your specific situation analyzing performance vs data integrity tradeoffs etc. Good luck.