I am inserting a new book into my book table and after trying to assign it to a many-to-many relation table. Imo this should run in a transaction.
(Because if the m2m insertion fails, the information about the realtionship is lost). My code now looks as follows and fails as i cannot access the BookUserXRefDao.insert(bookUser); query due to static context errors.
Is there an easy way to fix this?
#Transaction
public void insertBook(Book theBook, List<Integer> userIds){
long newBookId= insert(theBook);
//Insert into the m2m relation
BookUserXRef[] bookUser = new BookUserXRef[userIds.size()];
for (int i = 0; i < userIds.size(); i++) {
BookUserXRef[i] = new BookUserXRef(newBookId,userIds.get(i));
}
BookUserXRefDao.insert(bookUser);
}
Just realized that i can access the Singleton Database Instance from within my transaction.
Therefore i could just use
AppDb.getAppDb().BookUserXRefDao().insert(bookUser);
That solved the problem.
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.
I want to choose between native SQLiteDatabase and Realm to deal with a big amount of data.
For benchmark I add to storage 1 milion of Product entities:
{id:integer,sku:string,name:string,data_creating:string}
Using SQLiteDatabase it takes near 1 minute 34 seconds on my device.
Using Realm it takes more them 10 minutes.
My code is:
Realm realm = Realm.getInstance(getApplicationContext());
realm.beginTransaction();
for(int i = 0 ; i < 1000000;i++){
Product product = realm.createObject(Product.class);
product.setId(i+1);
product.setName("Product_"+i);
product.setSku("SKU__"+i);
product.setDateCreated(new Date());
}
realm.commitTransaction();
How can I improve my code for better time performance?
The original question spawned a discussion within Realm, and we ended up adding a faster method to insert objects. The code for creating and inserting 1 mio objects can now be written as:
final Product product = new Product();
final Date date = new Date();
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
for(int i = 0 ; i < 1000000; i++){
product.setId(i+1);
product.setName("Product_"+i);
product.setSku("SKU__"+i);
product.setDateCreated(date);
realm.insert(product);
}
}
});
}
You have to be aware that SQLite and Realm are two very different things. Realm is an object store and you are creating a lot of objects in the code shown above. Depending on your model class and the number of rows/objects, you will often see that Realm is a bit slower on inserts. To do a fair comparison, you could compare Realm with one of the many excellent ORMs out there.
Said that, Realm offers a low-level interface (io.realm.internal). I wouldn't recommend you to use it as it is currently undocumented. Your example would look like this:
long numberOfObjects = 1000000;
SharedGroup sharedGroup = new SharedGroup("default.realm");
WriteTransaction writeTransaction = sharedGroup.beginWrite();
Table table = writeTransaction.getTable("class_Product");
table.addEmptyRows(numberOfObjects);
for (int i = 0; i < numberOfObjects; i++) {
table.setLong(0, i, i); // id
table.setString(1, i, "Product_"+i); // name
table.setString(2, i, "SKU__"+i); // sku
table.SetDate(3, i, new Date()); // date
}
writeTransaction.commit();
sharedGroup.close();
You can now compare two table/row oriented data stores, and you will probably find that Realm is a bit faster than SQLite.
At Realm, we have a few ideas on how to get our object interface to run faster, and we hope to be able to implement them in the near future.
I am new to android and maybe its a silly question but i am not getting it. See i am designing a game in which we give scores to some persons. So i want to store the names of the persons in a database while installation and then their scores set to 0 initially which will be updated according to what the users select. Here i am not able to figure out that how should i enter the data as it will be around 100 names and their scores. Using INSERT INTO() statement will make it like 100 statements. So is there any short method like can we do it through strings or something. Just guessing though. Any help would be appreciated.
You don't hard-code names or scores into your SQL statements. Instead, you use parameters.
var command = new SQLiteCommand()
command.CommandText = "INSERT INTO Scores (name, score) VALUES(#name, #score)";
command.CommandType = CommandType.Text;
foreach (var item in data)
{
command.Parameters.Add(new SQLiteParameter("#name", item.Name));
command.Parameters.Add(new SQLiteParameter("#score", item.Score));
command.ExecuteNonQuery();
}
and then just loop through all of the names and scores.
I recommend you using a transaction.
You can archive this stating you want to use a transaction with beginTransaction(), do all the inserts on makeAllInserts() with a loop and if everything works then call setTransactionSuccessful() to do it in a batch operation. If something goes wrong, on the finally section you will call endTransaction() without setting the success, this will execute a rollback.
db.beginTransaction();
try {
makeAllInserts();
db.setTransactionSuccessful();
}catch {
//Error in between database transaction
}finally {
db.endTransaction();
}
For the makeAllInserts function, something like this could work out:
public void makeAllInserts() {
for(int i = 0; i < myData.size(); i++) {
myDataBase = openDatabase();
ContentValues values = new ContentValues();
values.put("name", myData.get(i).getName());
values.put("score", myData.get(i).getScore());
myDataBase.insert("MYTABLE", nullColumnHack, values);
}
}
If you also want to know about the nullColumnHack here you have a good link -> https://stackoverflow.com/a/2663620/709671
Hope it helps.
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 am trying to build a sqlite database. It has 2800 entries in them. When i try to insert, it takes a minute or so and later gives the system error message. The respective codes are given below.
In the create database java file,
ContentValues cv4 = new ContentValues();
public long createVariantEntry(String varid, String makeid, String modelid, String varname, String posteddate) {
cv4.put(VARIANT_ID, varid);
cv4.put(VARIANT_MAKE_ID, makeid);
cv4.put(VARIANT_MODEL_ID, modelid);
cv4.put(VARIANT_NAME, varname);
cv4.put(VARIANT_POSTED_DATE, posteddate);
return Database.insert(VARIANT_TABLE_NAME, null, cv4);
}
In the mainActivity,
for(int i = 0; i<build_emp_database.size();i++)
{
md.createVariantEntry(build_emp_database.get(i).get(0), build_emp_database.get(i).get(1), build_emp_database.get(i).get(2), build_emp_database.get(i).get(3), build_emp_database.get(i).get(4));
}
Also, just for 2800 entries, it is taking more than a minute, is there any way to speed them up?? I have several small databases, and have loaded them successfully. This is the only big database and its creating an issue while inserting. Please help where am i going wrong.
Don't try to run the insert operation on the UI thread, for starters.
I suggest you investigate using a content provider as a wrapper around your database. The ContentResolver object provides methods for doing operations in batch, and is in general a more robust way of working with databases. Use an IntentService to run the insert operation in the background.
If you do a lot insert operation, you need use the ContentProviderOperation to optimize your db operation. like these:
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
for(int i = 0; i<build_emp_database.size();i++) {
ContentProviderOperation.Builder builder =
ContentProviderOperation.newInsert(yourUrl);
builder.withValue(VARIANT_ID, varid)
.withValue(VARIANT_MAKE_ID, makeid)
...
ops.add(builder.build());
}
yourContentResolver.applyBatch(yourauthority, ops);