SQLITE Transactions : get total count - android

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

Related

Autoincrement value in transactions body

How can i get the autoincrement value in thansaction body?
Code
public void insertAllStudents(List<Student> students) {
String sql = "INSERT INTO "+ StudentEntry.TABLE_NAME +" VALUES (?,?,?,?,?);";
SQLiteDatabase db = this.getWritableDatabase();
SQLiteStatement statement = db.compileStatement(sql);
db.beginTransaction();
for (Student student: students) {
statement.clearBindings();
statement.bindString(2, student.getId());
statement.bindString(3, student.getFirstName());
statement.bindString(4, student.getLastName());
statement.bindLong(5, student.getBirthday());
statement.execute();
}
db.setTransactionSuccessful();
db.endTransaction();
}
The first column (_ID) is autoincrement field. Is it opportunity to get this value?
student.getId() -that's not id from database, that's different id.
If you change your code to use db.insert(), this method returns the id of the inserted row - see Get generated id after insert.
There is also a specialised SQLite function to get the last inserted row if you'd prefer to keep compiling statements, see Best way to get the ID of the last inserted row on SQLite
edit: example using db.insert(). This isn't tested but should be pretty close to functional.
db.beginTransaction();
boolean success = true;
final ContentValues values = new ContentValues();
for (final Student student: students) {
values.put("student_id", student.getId());
values.put("first_name", student.getFirstName());
values.put("last_name", student.getLastName());
values.put("birthday", student.getBirthday());
final long id = db.insert("my_table", null, values);
if (id == -1) {
success = false;
break;
}
// TODO do your thing with id here.
}
if (success) {
db.setTransactionSuccessful();
}
db.endTransaction();
Instead of statement.execute(), you can do statement.executeInsert(). This returns the row ID of the inserted row. Or, as #Tom suggested, you can use db.insert() instead, and it will also return the inserted row ID. Using a compiled statement like you are doing now is faster though.
If you want to try the db.insert() approach, it would look something like this:
ContentValues values = new ContentValues();
for (Student student: students) {
// use whatever constants you have for column names instead of these:
values.put(COLUMN_STUDENT_ID, student.getId());
values.put(COLUMN_STUDENT_FIRSTNAME, student.getFirstName());
values.put(COLUMN_STUDENT_LASTNAME, student.getLastName());
values.put(COLUMN_STUDENT_BIRTHDAY, student.getBirthday());
db.insert(StudentEntry.TABLE_NAME, null, values);
}

Thoughts about memory footprint

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();
}

SQLiteDatabase insert function returns -1

I have a database which is calling Medicine, there are four columns in it: id (primary key autoincrement), dId (foreign key), Name and Time. I try to add new row using insert function. This function must return id of this adding. But when I check this from debugger, I see that it returned -1.
Code:
ContentValues values = new ContentValues();
values.put("RECIPE_ID", recipeId);
values.put("MED_NAME", etMedName.getText().toString());
values.put("MED_TIME", etMedTime.getText().toString());
long mid = sqdb.insert("Medicine", null, values);
use
long mid = 0;
try
{
mid = sqdb.insertOrThrow("Medicine", null, values);
}
catch(SQLException e)
{
// Sep 12, 2013 6:50:17 AM
Log.e("Exception","SQLException"+String.valueOf(e.getMessage()));
e.printStackTrace();
}
It will tell you the probable Exception

SQLite Transaction Fail

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.)

Android database transactions

I am new to Android programming and am trying to understand the best practices.
I want to do multiple inserts into two different database tables, but as one transaction (as the tables shared a foreign key). I want my function to return a result so that I can display a Toast or something to say that an error occurred, otherwise I want to return the row ID of the first insert.
I believe one way of doing this is sort-of as follows (Disclaimer: psuedo-ish code, probably won't compile!):
Long result = -1;
myDatabase.beginTransaction();
try {
// Insert into first table
ContentValue someValues = new ContentValues();
someValues.put("dbfield1", 1);
result = myDatabase.insert(DATABASE_TABLE_1, null, someValues);
if (-1 != result ) {
// Insert into second table
someValues.clear();
someValues.put("dbfield2", 2);
if( myDatabase.insert(DATABASE_TABLE_2, null, someValues) < 0 ) {
result = -1;
}
}
mDatabase.setTransactionSuccessful();
} catch(Exception e) {
// An error occurred
result = -1;
} finally {
mDatabase.endTransaction();
}
Is there a simpler/better way of doing this?
You can override bulkInsert inside your ContentProvider
your Code looks fine. The Method should return the inserted Rows Value but you can customize that so you return only the first ID.
public int bulkInsert(Uri uri, ContentValues[] values) {
Log.e("BULK", "Bulk insert started for URI" + uri.toString());
bulkSqlDB = database.getWritableDatabase();
int numInserted;
bulkSqlDB.beginTransaction();
try {
for (ContentValues cv : values) {
insert(uri, cv);
}
bulkSqlDB.setTransactionSuccessful();
numInserted = values.length;
} finally {
getContext().getContentResolver().notifyChange(uri, null);
bulkSqlDB.endTransaction();
}
return numInserted;
}

Categories

Resources