insertWithOnConflict() vs. Insert or replace - android

I was refactoring my code and replacing all insert statements with insert or replacewhile i found the following method that uses db.insert() instead of a prepared statement:
public static void insertIntoTableByNameAndFieldsAndValues(
String tableName, String[] fields, String[] values, Context c)
throws FieldsAndValuesMismatchException, UnrecognizedTypeException {
DatabaseAbstractionLayer dal = new DatabaseAbstractionLayer(c);
SQLiteDatabase db = dal.getWritableDatabase();
ContentValues con = new ContentValues();
if (fields.length != values.length)
throw new FieldsAndValuesMismatchException(
"fieldsString and values are not the same size");
int fieldCount = fields.length;
for (int i = 0; i < fieldCount; i++) {
String[] fieldArr = fields[i].split(":");
String value = values[i];
if (fieldArr[1].equalsIgnoreCase("long")) {
// long
if(value.equalsIgnoreCase("null")){
con.putNull(fieldArr[0]);
}else{
con.put(fieldArr[0],(long) Integer.parseInt(value));
}
} else if (fieldArr[1].equalsIgnoreCase("string") || fieldArr[1].equalsIgnoreCase("text")) {
// string
if(value.equalsIgnoreCase("null")){
con.putNull(fieldArr[0]);
}else{
con.put(fieldArr[0], value);
}
}else if(fieldArr[1].equalsIgnoreCase("double")){
//double
if(value.equalsIgnoreCase("null")){
con.putNull(fieldArr[0]);
}else{
con.put(fieldArr[0], Double.valueOf(value));
}
} else {
throw new UnrecognizedTypeException(fieldArr[1]
+ " is not string,text, long or double");
}
}
db.insert(tableName, null, con);
db.close();
}
I don't want to rewrite the code and use a raw query.
Is there a way to replace a row when a conflict occurs? I guess insertWithOnConflict() would do that but I couldn't find a good example.

Yup, (change BaseColumns._ID to something suitable if you do not follow the standard name for the row identifier used by Android) and this should do what you want:
db.insertWithOnConflict(tableName, BaseColumns._ID, v,
SQLiteDatabase.CONFLICT_REPLACE);
But - that method sure does a lot of pointless things. In short, it could be boiled down to this:
public static void insertIntoTableByNameAndFieldsAndValues(
String tableName, String[] fields, String[] values, Context c)
throws FieldsAndValuesMismatchException {
if (fields.length != values.length) {
throw new FieldsAndValuesMismatchException(
"fields[] and values[] are not the same length");
}
ContentValues v = new ContentValues();
// You really don't need to distinguish between integers, doubles
// etc. etc. - SQLite is type agnostic, just dumping Strings from Java
// will work just fine - the *only* point in converting stuff is to check
// the format prior to inserts.
// Using "null" as the null identifier is also pointless. Pass actual null
// values instead - calling #putNull("somecolumn") is the same
// as #put("somecolumn", null)
for (int i = 0; i < fields.length; i++) {
v.put(fields[i], values[i]);
}
db.insertWithOnConflict(tableName, BaseColumns._ID, v,
SQLiteDatabase.CONFLICT_REPLACE);
}

Related

Android Studio: Unable to print cursor results

I am attempting to print the cursor results to log.d.
When I print the results of the cursor, it does not print the array.
i.e. D/Row values: com.example.androidlabs.Todo#7c9655f
Here is the code:
public void printCursor(Cursor c) {
//The database version number using db.getVersion for the version number.
int version = db.getVersion();
//The number of rows in the cursor
int rowCount = c.getCount();
//The number of columns in the cursor
int columnCount = c.getColumnCount();
//The names of the columns in the cursor
String[] columnNames = c.getColumnNames();
//The results of each row in the cursor
ArrayList<Todo> rowValuesList = new ArrayList<>();
int ColIndex = c.getColumnIndex(myOpener.COL_1);
int itemColIndex = c.getColumnIndex(myOpener.COL_2);
int urgentColIndex = c.getColumnIndex(myOpener.COL_3);
c.moveToFirst();
if(c.moveToFirst()) {
long id = c.getLong(ColIndex);
String item = c.getString(itemColIndex);
int urgentInt = c.getInt(urgentColIndex);
if (urgentInt == 1) {
urgent = true;
} else {
urgent = false;
}
rowValuesList.add(new Todo(item, urgent, id));
}
String rowValues = TextUtils.join(",", rowValuesList);
//Printing variables to log
Log.d("Database version", String.valueOf(version));
Log.d("Row count", String.valueOf(rowCount));
Log.d("Column count", String.valueOf(columnCount));
Log.d("Column names", Arrays.toString(columnNames));
Log.d("Row values", rowValues);
}
Other options I have tried that have not worked:
Log.d("Row values", rowValuesList.toString());
for (Todo t : rowValuesList) {
Log.d("Row values", String.valueOf(t));
}
StringBuilder s = new StringBuilder();
for(Todo todo : rowValuesList) {
s.append(todo);
s.append(",");
}
Log.d("Row values", String.valueOf(s));
I know the cursor is not empty as it is displays the results when loaded from SQLite on the application.
Any advice would be helpful.
Thank you,
Your output is:
D/Row values: com.example.androidlabs.Todo#7c9655f
That would make sense if:
rowValuesList contains a single Todo object, and
Your Todo class does not have a custom implementation of toString()
The default implementation of toString() that you inherit from Object gives a result like what you see: the fully qualified class name and an object ID, separated by #.

Getting specific value from Array which does not exist in database

I'm trying to get the value or data from the array that doesn't exists in the database.
public Cursor checkExistence(){
Cursor c=null;
String[] values={"headache","cold"};
SQLiteDatabase db= getReadableDatabase();
String query="SELECT * FROM "+TABLE_SYMPTOMS+" WHERE "+COLUMN_SYMP+" IN ("+toArrayRep(values)+")";
c=db.rawQuery(query,null);
Log.i("From Cursor","Cursor Count : " + c.getCount());
if(c.getCount()>0){
String val= c.getString()
Log.i("From Cursor","No insertion");
}else{
Log.i("From Cursor","Insertion");
}
db.close();
return c;
}
public static String toArrayRep(String[] in) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < in.length; i++) {
if (i != 0) {
result.append(",");
}
result.append("'" + in[i] + "'");
}
return result.toString();
}
In the String values={"headache","cold"} ,headache exists but cold does not exist in the database. From the code above, the Cursor returns Count=1 which is count>0 hence i can't insert into table.I would like to know how i can independently check whether the individual data exists, and the one which doesn't exist will be inserted into table.So in this case, "Cold" would be able to be inserted into the table.
If you use a single query to check all values, then what you get is a list of existing values, and you still have to search in the original list for any differences.
It is simpler to check each value individually:
String[] values = { "headache", "cold" };
SQLiteDatabase db = getReadableDatabase();
db.beginTransaction();
try {
for (String value : values) {
long count = DatabaseUtils.queryNumEntries(db,
TABLE_SYMPTOMS, COLUMN_SYMP+" = ?", new String[] { value });
if (count == 0) {
ContentValues cv = new ContentValues();
cv.put(COLUMN_SYMP, value);
db.insert(TABLE_SYMPTOMS, null, cv);
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
You need check Cursor.moveToFirst()
True = Have records in cursor.
False = Dont have records.
Example my code:
return database.query( table.getNameTable(),
table.getColumns(),
table.getWhereSelectTableScript(),
null,
table.getGroupBySelectTableScript(),
table.getHavingSelectTableScript(),
table.getOrderBySelectTableScript(),
table.getLimitRecordsSelectTableScript());
See more here !

I need help for getting SQLite data

I use this method for retrieving my data
public String getdata() {
String[] columns= new String[]{RowId,RowBusinessName};
Cursor c=OurDatabase.query(TableName,columns,null,null,null,null,null);
String Result="";
int iRowId=c.getColumnIndex(RowId);
int iRowBusinessName=c.getColumnIndex(RowBusinessName);
for(c.moveToFirst();!c.isAfterLast();c.moveToNext()){
Result=Result+c.getString(iRowBusinessName)+"\n";
}
return Result;
}
How can I make it return structured data (id & business_name)?
I want to display every business_name in a single textview.
Please help
If I understand what you are trying to do, here is the solution if you want to get only 1 RowBusinessName returned as a String. (Hoping that your RowBusinessName is type String).
public String getdata(int rowId) {
String[] columns= new String[]{RowId,RowBusinessName};
Cursor cursor = db.query(TABLENAME, columns, RowId + "=?", new String[]{rowId + ""}, null, null, null, null);
String Result="";
if (cursor != null && cursor.moveToFirst()) {
// not required though
int rowId = cursor.getInt(cursor.getColumnIndexOrThrow(RowId));
String rowBusinessName = cursor.getString(cursor.getColumnIndexOrThrow(RowBusinessName));
result = rowBusinessName;
}
return result;
}
Now if you want a list of RowBusinessName, then you have to build a List<String> rather than appending it to Result. That's not really a good way!
public List<String> getAll() {
List<String> businessNameList = new ArrayList<String>();
String[] columns= new String[]{RowId,RowBusinessName};
Cursor c=OurDatabase.query(TableName,columns,null,null,null,null,null);
if (c != null && c.moveToFirst()) {
// loop until the end of Cursor and add each entry to Ticks ArrayList.
do {
String businessName = cursor.getString(cursor.getColumnIndexOrThrow(RowBusinessName));
if (businessName != null) {
businessNameList.add(businessName);
}
} while (c.moveToNext());
}
return businessNameList;
}
These are work around.
The appropriate answer would be to create an Object that holds id and businessName. That way, you build an object from DB and just return the entire Object.

Two Cursors, ListView, CursorIndexOutOfBoundsException

As a beginner in android java world I need your help. I've got problem with famous "CursorIndexOutOfBoundsException".
I'm using SQLite db, and I have two cursors, I'm getting some rows from database.
I need to get some previous values with some conditions with second cursor (c2) and put these values on ListView.
Code works with one exception:
"android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0".
ListView looks OK if I just ignore this exception but I want to fix it.
I know it is connected to with Cursor. I tried to check some conditions - it didn't help. Maybe if you could take a look at my code you find where is the cause.
Code:
public void LoadLogGrid()
{
dbHelper=new DatabaseHelper(this);
try
{
int LogName = (int) spinLog.getSelectedItemId();
Cursor c=dbHelper.getLogByLogID(LogName);
if (c != null) c.moveToFirst();
int count = c.getCount();
if (c.moveToFirst()){
ArrayList<String> mArrayList = new ArrayList<String>();
int i=0;
do {
int sVar1 = c.getInt(c.getColumnIndex("Var1"));
Long sId = (long) c.getInt(c.getColumnIndex("_id"));
Cursor c2=dbHelper.getPrevLogByLogID(LogName,sVar1);
c2.moveToFirst();
if (c2!=null) {
String sPrevOdo = c2.getString(c2.getColumnIndex("Odo"));
mArrayList.add(sPrevOdo);
c2.close();
} else {
//stopManagingCursor(c2);
//c2.close();
Log.d("A:", "Something");
}
String [] from=new String []{"Date","Col1","Col2","Col3"};
int [] to=new int [] {R.id.logDate,R.id.logCol1,R.id.logCol2,R.id.logCol3,R.id.rowOpt2};
SimpleCursorAdapter sca=new LogCursorAdapter(this,R.layout.loggridrow,c,from,to,mArrayList);
grid.setAdapter(sca);
registerForContextMenu(grid);
i++;
} while (c.moveToNext());
c.close();
dbHelper.close();
}
}
catch(Exception ex)
{
AlertDialog.Builder b=new AlertDialog.Builder(this);
b.setMessage(ex.toString());
b.show();
}
}
Query in second cursor:
public Cursor getPrevLogByLogID(long LogID, long Var1)
{
SQLiteDatabase db=this.getReadableDatabase();
String[] params=new String[]{String.valueOf(LogID),String.valueOf(Var1)};
Cursor c2=db.rawQuery("SELECT LogID as _id, Col1 from Log WHERE Col2=? AND Col3<? AND Full=1 ORDER BY Odo DESC", params);
if (c2 != null) { c2.moveToFirst();}
return c2;
}
Try changing your
do {
....
} while (c.moveToNext());
to
while (c.moveToNext()) {
....
}
The way it is now, it will run the loop at least once no matter what.
you have moved the c2 to the position as c2.moveToFirst() and after that you are doing the checking process whether it is null or not and i think the cursor should be null so that the exception is raised try putting the checking condition before moving the cursor to the first position

Logging SQL queries in android

I am using the query functions in order to build the SQL queries for my tables. Is there a way to see the actual query that is run? For instance log it somewhere?
So far the best I could do was to have a look at the cursor's member mQuery using a breakpoint. I'd love to output the queries automatically though. This member is of course not public and does not have a getter.
Just for the record, here is an implementation of the accepted answer.
/**
* Implement the cursor factory in order to log the queries before returning
* the cursor
*
* #author Vincent # MarvinLabs
*/
public class SQLiteCursorFactory implements CursorFactory {
private boolean debugQueries = false;
public SQLiteCursorFactory() {
this.debugQueries = false;
}
public SQLiteCursorFactory(boolean debugQueries) {
this.debugQueries = debugQueries;
}
#Override
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
String editTable, SQLiteQuery query) {
if (debugQueries) {
Log.d("SQL", query.toString());
}
return new SQLiteCursor(db, masterQuery, editTable, query);
}
}
adb shell setprop log.tag.SQLiteStatements VERBOSE
Don't forget to restart your app after setting this property.
It is also possible to enable logging of execution time. More details are availabe here: http://androidxref.com/4.2.2_r1/xref/frameworks/base/core/java/android/database/sqlite/SQLiteDebug.java
You can apply your own SQLiteDatabase.CursorFactory to the database. (See the openDatabase parameters.) This will allow you to create your own subclass of Cursor, which keeps the query in an easily accessible field.
edit: In fact, you may not even have to subclass Cursor. Just have your factory's newCursor() method return a standard SQLiteCursor, but log the query before doing so.
adb shell setprop log.tag.SQLiteLog V
adb shell setprop log.tag.SQLiteStatements V
adb shell stop
adb shell start
Using an SQLiteQueryBuilder it's painfully simple. buildQuery() returns a raw sql string, which can then be logged:
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(ExampleTable.TABLE_NAME);
String sql = qb.buildQuery(projection, selection, null, null, sortOrder, null);
Log.d("Example", sql);
So far the best I could do was to have a look at the cursor's member mQuery using a breakpoint. This member is of course not public and does not have a getter, hence, no way to output it. Any better suggestion?
If you are using SQLiteDatabase with it's standard methods as insert, update and delete custom CursorFactory will not be working.
I implemented my not very great but working solution based on SQLiteDatabase class. It just repeats logic of insert, update and delete methods but without statements and actually doing the logging of SQL statements.
public class SQLiteStatementsLogger {
private static final String TAG = SQLiteStatementsLogger.class.getSimpleName();
private static final String[] CONFLICT_VALUES = new String[]
{"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
public void logInsert(String table, String nullColumnHack, ContentValues values) {
logInsertWithOnConflict(table, nullColumnHack, values, 0);
}
public static void logInsertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
StringBuilder sql = new StringBuilder();
sql.append("INSERT");
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(" INTO ");
sql.append(table);
sql.append('(');
Object[] bindArgs = null;
int size = (initialValues != null && initialValues.size() > 0)
? initialValues.size() : 0;
if (size > 0) {
bindArgs = new Object[size];
int i = 0;
for (String colName : initialValues.keySet()) {
sql.append((i > 0) ? "," : "");
sql.append(colName);
bindArgs[i++] = initialValues.get(colName);
}
sql.append(')');
sql.append(" VALUES (");
for (i = 0; i < size; i++) {
sql.append((i > 0) ? ",?" : "?");
}
} else {
sql.append(nullColumnHack + ") VALUES (NULL");
}
sql.append(')');
sql.append(". (");
for (Object arg : bindArgs) {
sql.append(String.valueOf(arg)).append(",");
}
sql.deleteCharAt(sql.length()-1).append(')');
Log.d(TAG, sql.toString());
}
public static void logUpdate(String table, ContentValues values, String whereClause, String[] whereArgs) {
logUpdateWithOnConflict(table, values, whereClause, whereArgs, 0);
}
public static void logUpdateWithOnConflict(String table, ContentValues values,
String whereClause, String[] whereArgs, int conflictAlgorithm) {
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(table);
sql.append(" SET ");
// move all bind args to one array
int setValuesSize = values.size();
int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
Object[] bindArgs = new Object[bindArgsSize];
int i = 0;
for (String colName : values.keySet()) {
sql.append((i > 0) ? "," : "");
sql.append(colName);
bindArgs[i++] = values.get(colName);
sql.append("=?");
}
if (whereArgs != null) {
for (i = setValuesSize; i < bindArgsSize; i++) {
bindArgs[i] = whereArgs[i - setValuesSize];
}
}
if (!TextUtils.isEmpty(whereClause)) {
sql.append(" WHERE ");
sql.append(whereClause);
}
sql.append(". (");
for (Object arg : bindArgs) {
sql.append(String.valueOf(arg)).append(",");
}
sql.deleteCharAt(sql.length()-1).append(')');
Log.d(TAG, sql.toString());
}
public static void logDelete(String table, String whereClause, String[] whereArgs) {
StringBuilder sql = new StringBuilder("DELETE FROM " + table);
if (!TextUtils.isEmpty(whereClause)) {
sql.append(" WHERE " + whereClause);
sql.append(". (");
for (Object arg : whereArgs) {
sql.append(String.valueOf(arg)).append(",");
}
sql.deleteCharAt(sql.length()-1).append(')');
}
Log.d(TAG, sql.toString());
}
}
Be aware not to use the logger in release versions. It might increase time of queries executing.
You can check if the build is in debug mode with this code line:
0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)
If it is for once off scenario, I would suggest injecting an error (e.g. type in expression like LIEK instead of LIKE!) and watch the Eclipse LogCat for any errors! HTH!
If you are using a ContentProvider to access the DB, this is how I got it logging the queries. Not a perfect solution, but it works for development
#Override
public boolean onCreate() {
dbHelper = new MySQLiteHelper(getContext());
database=dbHelper.getWritableDatabase();
if(!database.isReadOnly())
database.execSQL("PRAGMA foreign_keys=ON;");
return true;
}
SQLiteDatabase.CursorFactory cursorFactory = new SQLiteDatabase.CursorFactory() {
#Override
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) {
Log.d(TAG, "Query: "+query);
return new SQLiteCursor(db, masterQuery, editTable, query);
}
};
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
String table =getTableName(uri);
if(Constants.LOG_QUERIES){
database = SQLiteDatabase.openOrCreateDatabase(database.getPath(), cursorFactory);
}
Cursor cursor =database.query(table, projection, selection, selectionArgs, null, null, sortOrder);
cursor.moveToFirst();
return cursor;
}
It'll throw a DatabaseNotClosed exception, but you'll be able to see the query
Personnally I log text using java.util.Log and the Log.w("MYAPPNAME", "My text...") function. It shows up in the Log view of Eclipse and it can be filtered to output only the logs for "MYAPPNAME".

Categories

Resources