Android SQLite batch insertion - android

I need to insert a lot of rows in the database at a time and I'm seeking for the most efficient way to do it. I have seen code like this:
db.beginTransaction();
for (ModelObject object : modelObjectsCollection){
ContentValues values = new ContentValues();
... // fill values variable with values from object
db.insert(TABLE_NAME, null, values);
values.clear();
}
db.setTransactionSuccessful();
In this case all inserts are accomplished as a single operation which takes less time. Will it still work as a single operation if I incapsulate insertion of a single row into a method like this:
public void insertAllRows(){
db.beginTransaction();
for (ModelObject object : modelObjectsCollection){
insertSingleRow(object);
}
db.setTransactionSuccessful();
}
public void insertSingleRow(ModelObject object){
ContentValues values = new ContentValues();
... // fill values variable with values from object
db.insert(TABLE_NAME, null, values);
}
Will it be accomplished in a single transaction as well?
Besides, I do not understand: is it correct that if we do not call
db.beginTransaction(); ... db.setTransactionSuccessful();
explicitly, but only call db.insert(), beginTransaction()-setTransactionSuccessful() methods are invoked inside insert(). In contrary, if we invoke insert() between invokations of beginTransaction()-setTransactionSuccessful(), the latter 2 methods aren't invoked inside insert()?

You can execute queries without transactions at all. And there are no need in transaction for single query at all.
Also you need to call db.endTransaction(); after db.setTransactionSuccessful() for commiting it.
upd: you can find more about common transaction use here http://www.4js.com/online_documentation/fjs-fgl-manual-html/User/Transactions.html for example.

Related

Do I have to close the Cursor object whenever I call rawQuery()?

I'm planning to use Android SQLite for the first time.
Cursor c = db.rawQuery("some select command here", null);
// some jobs with the cursor..
c = db.rawQuery("another select command here", null);
// some jobs with the cursor..
c.close();
db.close();
c = null;
db = null;
As you can see, I'm trying to call rawQuery() method several times.
Do I have to close the cursor before I call rawQuery() method AGAIN?
Do I have to assign null to the variables after closing the cursor and database like above?
Do I have to close the cursor before I call rawQuery() method AGAIN?
Close the cursor whenever you are finished reading from it. This is to release resources that are opened by the cursor, so yes, you should close the first cursor before the second query.
Do I have to assign null to the variables after closing the cursor and database like above?
It depends on the scope of the variables. If your code looks like this...
class Foo {
void doSomething() {
SQLiteDatabase db = ...
Cursor c = db.rawQuery...
// other stuff
c.close();
db.close();
}
}
... then there's really no point nulling them out because they will go out of scope immediately when the method finishes execution. But your actual code might look different. If you have some reason for wanting to allow those objects to be garbage collected, then you can null out the variables.

Access values of SQLite loader in thread

I'm using SQLIte loader (cwac-loaderex). i'm performing set of operation in a background thread. the operation includes inserting new record, updating the existing one as well as deleting the record.
These operations i'm performing on cursor object like this. these methods are present in loader class & i'm accessing these from my background thread.
#Override
public void Insert(String table, String nullColumnHack,
ContentValues values) {
mLoader.insert(table, nullColumnHack, values);
}
#Override
public void Update(String table, ContentValues values,
String whereclause, String[] whereargs) {
// TODO Auto-generated method stub
mLoader.update(table, values, whereclause, whereargs);
}
In few scenarios like. Once i insert record, i need the primary key to update another record. i'm failing to get those. Once thread completes its execution the UI will get updated and i'm able to access the required value onPostexecute method. But i want the values soon after i insert to Database.
As per i know, that to reflect the updated values i'm calling the insert, update & delete method directly on Loader object.
How to access the inserted row, in the same thread soon after insert is called?

query() is called again after notifyChange()

I am using a custom content provider and a CursorLoader for displaying a list of elements in a fragment.
First the CursorLoader calls the ContentProvider's query() to get all elements stored in my Database. In my query() function I start a thread which does a WebService call to update the elements in my database. The thread is parsing the WebServices' response and calling my ContentProviders bulkInsert().
When my bulkInsert() is done, i call notifyChange().
Now here is whats happening: I see that after the notifyChange(), my ContentProvider's query() method is called again, which leads to a new WebService call and so on and so on.
Here my query() method in the ContentProvider:
...
// Database query
ticketCursor = mDb.query(TBL_TICKET_NAME, projection, selection, selectionArgs, null, null, orderBy);
// set Notification URI
ticketCursor.setNotificationUri(getContext().getContentResolver(), TICKET_CONTENT_URI);
// WebService call --> This starts a new Thread for the WebService call
asyncSoapQuery(where, uri);
return ticketCursor;
My bulkInsert method looks like this:
mDb.beginTransaction();
for (ContentValues value : values) {
mDb.insertWithOnConflict(table, null, value, SQLiteDatabase.CONFLICT_REPLACE);
}
mDb.setTransactionSuccessful();
getContext().getContentResolver().notifyChange(TICKET_CONTENT_URI, null);
mDb.endTransaction();
return values.length();
So, my problem is that this causes an endless loop of webservice calls. I thought, that the notifyChange() is not calling the content provider's query() method again. I only want to do the WS call inside my query() again if the user hits "refresh" on my UI...
What am I missing here?
Thanks in advance for your help!
I have the same issue but I'm using insert() instead of applyBatch() in the content provider. I manage to resolve my issue by changing
mDb.insertWithOnConflict(table, null, value, SQLiteDatabase.CONFLICT_REPLACE);
to
long rowId = mDb.insertWithOnConflict(table, null, value, SQLiteDatabase.CONFLICT_IGNORE);
My next step is to check if rowId is greater than zero. Only when it is greater than zero then execute
getContext().getContentResolver().notifyChange
So it is something like this:
if (rowId > 0) {
notifyChange(uri);
return uri;
} else {
return null;
}

Android SQLite: Database empty in second activity

I've searched all over the net for a solution to this so lets hope someone here can help.
My app has a start up task which populates a SQLite database before loading the main menu. A second activity that can be accessed from the main menu needs access to this. Therefore I close the database in the first activity in order to stop locking errors.
For some reason, the database seems to have no rows as soon as I close the connection, in both the same activity and the second activity.
Heres a code sample:
SQLiteDatabase db = getWritableDatabase(); // get instance of current database
db.beginTransaction(); // set exclusive mode to speed up
for(GulbArticle g : gulbArticles){
this.insert(g);
}
db.setTransactionSuccessful();
// counting here returns 315 rows using the all2() function below
db.close();
// counting here returns 0 rows using the all2() function below
Here is a function I made to get the count back
public void all2(){
SQLiteDatabase db = getReadableDatabase();
String sql = "SELECT COUNT(*) FROM "+TABLE_NAME;
SQLiteStatement statement =db.compileStatement(sql);
long count = statement.simpleQueryForLong();
Log.v("ccount2",count+"");
}
So in both cases I'm initializing an instance of the database, but for some reason as soon as I close it once I can't reopen it/there seems to be nothing in the database. Maybe I'm missing something simple but this has really stumped me.
It seems you forgot to call db.endTransaction();, i.e. to commit the transaction.
It should read:
db.setTransactionSuccessful();
db.endTransaction();
db.close();
Also it is good idea to surround it in try-catch-finaly like this:
try {
db.beginTransaction();
// do your DB manipulation
db.setTransactionSuccessful();
} catch(...) {
...
} finally {
db.endTransaction();
}
db.close();
You don't need to close database when you start second activity. SQLite is normal DB management system - which means that these kind of situation should be resolved.
Resolving of this usually done by transactions mechanism - which you're tried to use in your code.
In your case I believe you have to use non-eclusive transactions - i.e. when record is locked in exclusive mode - record in unaccessible, so basic idiom should looks like:
db.beginTransactionNonExclusive();
try
{
// do smth
db.setTransactionSuccessful();
}
finally
{
db.endTransaction();
}

Android Contentprovider - update within an insert method

Is it ok to call the SQLiteDatabase update method in the insert() overridden method of a content provider?
Basically it's fine, but since you didn't provided code, I just can post 2 possible ways for it
First:
// In your content provider
public Uri insert(...) {
long insertId = db.insert(...);
if(insertId == -1) {
// insert failed, do update
db.update(...);
}
}
Second:
public Uri insert(...) {
long insertId = db.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_REPLACE)
if(insertId == -1) {
// insert and update/replace failed
}
}
Check out SQLiteDatabase for reference on the forth parameter. In most cases the last method should be sufficient, unless you want only certain fields being updated if the row exists and have all fields if it doesn't.
Most useful need for insertWithOnConflict may be, that you could insert a row and if it already exists, ignore the insert and instead return the Uri/primary key of the already existing row.
It's your choice what you write in your overridden methods.
So yes, it is ok.
I don't know what you're trying to do, but you might want to to take a look on the SQLiteDatabase's replace() method too. Maybe it better suits your needs.

Categories

Resources