i am working on sqlite insertion using contentvalues with transaction . The following code does not generate any exception however the data is not inserted.
Did i miss somethings ? Thanks.
public boolean addRecord(Rec rec) {
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
ContentValues values = new ContentValues();
values.put(KEY_ID, rec.get());
// Inserting Row
try {
db.insertOrThrow(TABLE_RECORDS, null, values);
} catch (SQLException e) {
e.printStackTrace();
}
db.endTransaction();
db.close();
return true;
}
After calling beginTransaction, you must call setTransactionSuccessful to ensure that the transaction gets committed. Without that call, any changes in the transaction are rolled back.
db.beginTransaction();
try {
...
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
This particular construction ensures that any exception in the actual database code will result in an rollback.
(If you get an exception, it is a bad idea to just swallow it; the entire function must fail.)
Related
A very pertinent question (at least I think it is) came to me while developing an Android app.
Example
We are inserting 10000 rows on a db (at once).
db.beginTransaction();
try{
for(Object toInsert: listOfObjects) {
ContentValues values = new ContentValues();
//put the values on the object
values.put(key, toInsert.getValue());
db.insert(columnName, null, values);
}
db.setTransactionSuccessful();
} catch(Exception e) {
//handle exception
} finally {
db.endTransaction();
}
We are creating 10000 new ContentValue objects on the loop. And object creation is very expensive to the VM.
And if we modify this a little bit?
Different Approach
ContentValues values, hack = new ContentValues();
db.beginTransaction();
try{
for(Object toInsert: listOfObjects) {
values = hack;
//put the values on the object
values.put(key, toInsert.getValue());
db.insert(columnName, null, values);
}
db.setTransactionSuccessful();
} catch(Exception e) {
//handle exception
} finally {
db.endTransaction();
}
In this second example, we are making a 'reset' to the value object, because that will be used in every single row.
And so, my question is: Am I doing this right? With the second approach I'm optimizing the process without leaving a big memory footprint? If not, why? Have you some suggestions/thoughts about this?
You are doing it wrong with the two variables.
Consider the following case:
In first iteration, values = new instance, hack = new instance. OK.
after you do values = hack. values and hack both refer to the same memory location now. So there is no point in creating two variables.
You could simply do following:
ContentValues values = new ContentValues();
db.beginTransaction();
try{
for(Object toInsert: listOfObjects) {
//put the values on the object
values.put(key, toInsert.getValue());
db.insert(columnName, null, values);
}
db.setTransactionSuccessful();
} catch(Exception e) {
//handle exception
} finally {
db.endTransaction();
}
ATM when I write to the SQLite in my android app, I do it this way:
try {
for (User user: users) {
ContentValues values = new ContentValues();
databaseManager.database.beginTransaction();
values.put("user_name", user.getName());
values.put("user_email", user.getEmail());
databaseManager.database.insert("users", null, values);
}
databaseManager.database.setTransactionSuccessful();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
databaseManager.database.endTransaction();
}
But when I read from the DB, I dont use begin, setsuccessful and end:
Cursor cursor = databaseManager.database.rawQuery(SQLQueries.getUsers(), null);
if (cursor.moveToFirst()) {
if (cursor!=null) {
do {
User user = new User();
try {
user.setName(cursor.getString(cursor.getColumnIndexOrThrow("user_name")));
user.setEmail(cursor.getString(cursor.getColumnIndexOrThrow("user_email")));
} catch (Exception ex) {
ex.printStackTrace();
}
users.add(user);
} while (cursor.moveToNext());
}
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
cursor = null;
}
Should I add beginTransaction, setTransactionSuccessful and endTransaction to the read operations as well? Im pretty sure I shouldnt, but I need to be 100% on this one.
When you are not using explicit transactions, SQLite will automatically wrap a transaction around every statement.
When you write to the database, every insert/update/delete call is a single statement.
If you are doing multiple such operations, you use transactions to avoid paying the transaction overhead for each of them.
A query (query or rawQuery) is a single statement, even if it returns multiple rows.
Therefore, using a transaction around a single query does not make any difference.
(If you have multiple queries, you could use a transaction to ensure that their results are consistent with each other even if another thread attempts to change the database between them.)
I am using transactions to insert record to my database. Can you please tell me whether this is the right way to get total inserted record (return by numrow)? Also, in the code below, if some insert fails, will it continue to next insertion or will exit (I didn't use endTransaction in Catch block)?
int numrow = 0;
try{
db.beginTransaction();
for(mylibman cn : insertlist){
ContentValues values = new ContentValues();
values.put(KEY_LIBID, cn.getLibid());
values.put(KEY_NAME, cn.getBookname());
db.insertWithOnConflict(TABLE_NAME, null, values,SQLiteDatabase.CONFLICT_IGNORE);
numrow++;
}
db.setTransactionSuccessful();
}catch (Exception e) {
} finally {
db.endTransaction();
}
return numrow;
you should check like this
int numrow = 0;
try{
db.beginTransaction();
for(mylibman cn : insertlist){
ContentValues values = new ContentValues();
values.put(KEY_LIBID, cn.getLibid());
values.put(KEY_NAME, cn.getBookname());
//do like this
long insertedId=db.insertWithOnConflict(TABLE_NAME, null, values,SQLiteDatabase.CONFLICT_IGNORE);
if(insertedId!=-1)
{
numrow++;
}
}
db.setTransactionSuccessful();
}catch (Exception e) {
} finally {
db.endTransaction();
}
return numrow;
Your code will also count records that were ignored.
insertWithOnConflict returns the rowid of the inserted record, or -1, so you have to check for that:
if (db.insertWithOnConflict(TABLE_NAME, null, values,
SQLiteDatabase.CONFLICT_IGNORE) != -1)
numrow++;
The insertWithOnConflict function does not throw an exception if a record is not inserted due to a conflict. However, it will throw if there is some other error, such as an unknown column name, or a read-only database.
You should not blindly ignore exceptions; just use try/finally:
db.beginTransaction();
try {
for (...) {
...
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Non of the answers here reflect the documentation:
"returns:
the row ID of the newly inserted row OR the primary key of the existing row if the input param 'conflictAlgorithm' = CONFLICT_IGNORE OR -1 if any error"
Since the author wants to know the 'inserted' columns, both answers are wrong. The number inserted rows could be 0, as they already exist. However "CONFLICT_IGNORE" will make insert return the existing primary key, so the numrow will be the rows you 'tried' to insert and didn't fail, but not the rows 'inserted'.
You might have to use the flag "SQLiteDatabase.CONFLICT_ABORT" or "SQLiteDatabase.CONFLICT_FAIL" depending on what and how you insert.
Source: http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#insertWithOnConflict%28java.lang.String,%20java.lang.String,%20android.content.ContentValues,%20int%29
So I have a database, SQLiteDatabase db I am writing a couple private methods in my manager class that will be called by a public method:
public void updateData (MakeabilityModel newData){
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
try {
reWriteSVTable(db, list);
db.setTransactionSuccessful();
} catch (Exception e){
//TODO through rollback message?
e.printStackTrace();
} finally {
db.endTransaction();
}
}
//Private Methods
private void clearTable(SQLiteDatabase db, String table){
db.delete(table, null, null);
}
private void reWriteSVTable(SQLiteDatabase db, List<MakeabilityLens> lenses){
clearTable(db, singleVision);
ContentValues cv;
for(int i=0; i<lenses.size(); i++){
cv = new ContentValues();
cv.put(colScreenID, hsID);
cv.put(colIconID, id);
cv.put(colRank, hsTotal);
db.insert(isLookUp, colID, cv);
}
}
My question is this.. i want to be able to throw sql exceptions back to the public method so that if there is an exception, it will kill the transaction and rollback ALL data..
it appears that using delete() and insert() methods are cleaner than execSQL() but don't throw sqlExceptions. execSQL() on the other hand does? do i need to uses execSQL and how do I insure that should it throws an exception in any of the private methods that it will catch it and roll it back in the private method
first of all execSQL() throws an exception if the sql string is not valid. that is the exception is on the sql string syntax NOT the sql operation. that is, it will not throw an exception if the sql statement is valid but the operation failed (because of a constraint for example).
So ..
basically the only difference between execSQL() and delete() is that delete() returns the number of rows affected (in your case, the number of deleted rows), but execSQL() doesn't.
Note:
for delete() to return the number of rows affected, you have to pass any value other than null in the where clause parameter. In your case, pass "1".
Is there a way to prevent the sqlite db to crash? I have a listview with items in it. When you click on a button al items are saved in a db. But when there are a lot of products in the list and you click verry fast on the save button the application freezes. And when I check my database afterwards all the items are saved multiple times.
Someone knows the answer?
Code:
With isProdAlreadyFavorite I check if the db already got an entry with the id...
When you click on the button he executes this function..
public void addFavProducten(ArrayList<Product> producten) {
db = this.getWritableDatabase();
db.beginTransaction();
try {
for (Product product : producten) {
if (!isProdAlreadyFavorite(product.getProductid())) {
ContentValues cv = new ContentValues();
cv.put(Constanten.FAV_PROD_COLUMN_PRODUCTID,
product.getProductid());
db.insert(Constanten.TABLE_FAVPROD, null, cv);
}
}
db.setTransactionSuccessful();
} catch (Exception e) {
db.endTransaction();
} finally {
db.endTransaction();
db.close();
}
}