Content provider updating all rows - android

working on a content provider and I'm having an issue with it. When I try to update a certain row in the SQLite database through the content provider, it updates the column in all the rows, not just the row I specify. I know the CP is working because I can access it, populate a listview with it, and change the content of column, but never just one column.
Here is the relevant update method
public int update(Uri url, ContentValues values, String where,
String[] whereArgs) {
SQLiteDatabase mDB = dbHelper.getWritableDatabase();
int count;
String segment = "";
switch (URL_MATCHER.match(url)) {
case ITEM:
count = mDB.update(TABLE_NAME, values, where, whereArgs);
break;
case ITEM__ID:
segment = url.getPathSegments().get(1);
count = mDB.update(TABLE_NAME, values,
"_id="
+ segment
+ (!TextUtils.isEmpty(where) ? " AND (" + where
+ ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URL " + url);
}
getContext().getContentResolver().notifyChange(url, null);
return count;
}
and here is the code I use to (try to) update it.
ContentValues mUpdateValues = new ContentValues();
mUpdateValues.put(ContentProvider.HAS, "true");
mUpdateValues.put(ContentProvider.WANT, "false");
mRowsUpdated = getContentResolver().update(Uri.parse(ContentProvider._ID_FIELD_CONTENT_URI
+ rowId), mUpdateValues, null, null);
and here is the URI
URL_MATCHER.addURI(AUTHORITY, TABLE_NAME + "/#", ITEM__ID);
Thanks, any help would be appreciated.
EDIT I have also tried
mRowsUpdated = getContentResolver().update(
ContentProvider._ID_FIELD_CONTENT_URI, mUpdateValues,
null, null);
and
mRowsUpdated = getContentResolver().update(
ContentProvider.CONTENT_URI, mUpdateValues,
null, null);

You are not specifying a WHERE clause, which is what is used to update only specific rows. The default behavior of content providers is to update all the rows, unless you specify conditions.
From the docs:
developer.android.com/reference/android/content/ContentResolver.html
Parameters
uri The URI to modify.
values The new field values. The key is the column name for the field. A null value will remove an existing field value.
where A filter to apply to rows before updating, formatted as an SQL WHERE clause (excluding the WHERE itself).

Related

Update row in SQlite database by row position in android

I have database which contains "date" column and "item" column.
I want that user could update specific row in the database.
I trying to do it with update method in SQLiteDatabase class.
My problem is that i dont know how to make update method find exactly the row i want.
I saw some example that use it with parameters from one word.
like this:
ourDatabase.update(tableName, cvUpdate, rowId + "=" + item , null);
My problem is that i want to update the row that have specific item and date. so the name of the item alone is not enough.
I tried this code below but its didnt work, hope youll can help me.
public void updateEntry(String item, String date) throws SQLException{
String[] columns = new String[]{myItem, myDate};
Cursor c = ourDatabase.query(tableName, columns, null, null, null, null, null);
long position;
ContentValues cvUpdate = new ContentValues();
cvUpdate.put(date, myDate);
cvUpdate.put(item, myExercise);
int itemAll = c.getColumnIndex(myItem);
int dateAll = c.getColumnIndex(myDate);
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()){
if (c.getString(itemAll).equals(myItem) && c.getString(dateAll).equals(myDate))
{
position = c.getPosition();
break;
}
}
ourDatabase.update(tableName, cvUpdate, rowId + "=" + position , null);
}
First, the columns String[] is supposed to contain column names, such as "_ID", or whatever are the column names you have used. Given that you compare the content of the column myItem with the object myItem, I assume there is a confusion somewhere here.
Secondly, rowId and position are different things in SQL, especially if you delete rows, as the row id usually is autoincrement, and especially since your query is not explicitely sorted. Replacing c.getPosition() by c.getLong(c.getColumnIndex(ID_COLUMN)) would make more sense.
Thirdly, sql is nice because you can query it. For example, rather than get all items and loop to find the matching date and item, you can :
String whereClause = ITEM_COLUMN + " = ? and " + DATE_COLUMN + " = ?";
String[] whereArgs = new String[] { item, date };
Cursor c = ourDatabase.query(tableName, columns, whereClause, whereArgs, null, null, null);
instead of your for loop.
Forthly, you can even make the query in the update :
String whereClause = ITEM_COLUMN + " = ? and " + DATE_COLUMN + " = ?";
String[] whereArgs = new String[] { item, date };
ourDatabase.update(tableName, cvUpdate, whereClause, whereArgs);
Extra tip: use full caps variable names for contants such as column names, it help with readability.

Updating a single column is creating sqlite syntax error

I'm not sure what I'm doing wrong, but I'm trying to update a single integer value in a column of a table to 1 from 0. When creating the database, I set all values of the column to zero using:
for (int i = 0; i < setups.length; i++) {
ContentValues values = new ContentValues();
values.put(JokeDbContract.TblJoke.COLUMN_NAME_SETUP, setups[i]);
values.put(JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE, punchlines[i]);
values.put(JokeDbContract.TblJoke.COLUMN_NAME_USED, 0);
db.insert(JokeDbContract.TblJoke.TABLE_NAME, null, values);
}
Then, in the actual activity, I'm doing:
private void findNewJoke() {
JokeDb jokeDb = JokeDb.getInstance(this);
SQLiteDatabase theDb = jokeDb.getDB();
String selection = JokeDbContract.TblJoke.COLUMN_NAME_USED + "=" + 0;
// Query database for a joke that has not been used, update the fields
// theJoke and thePunchline appropriately
String[] columns = {JokeDbContract.TblJoke._ID,
JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE,
JokeDbContract.TblJoke.COLUMN_NAME_SETUP,
JokeDbContract.TblJoke.COLUMN_NAME_USED};
Cursor c = theDb.query(JokeDbContract.TblJoke.TABLE_NAME, columns, selection,
null, null, null, null);
if (c.moveToFirst() == false) {
Toast.makeText(this, R.string.error_retrieving_joke, Toast.LENGTH_LONG).show();
Log.e(getString(R.string.app_name),"No jokes retreived from DB in JokeActivity.findNewJoke()!");
}
else {
ContentValues values = new ContentValues();
theSetup = c.getString(c.getColumnIndexOrThrow(JokeDbContract.TblJoke.COLUMN_NAME_SETUP));
thePunchline = c.getString(c.getColumnIndexOrThrow(JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE));
String updateSelection = JokeDbContract.TblJoke.COLUMN_NAME_SETUP + "=" + theSetup;
values.put(JokeDbContract.TblJoke.COLUMN_NAME_USED, 1);
theDb.update(JokeDbContract.TblJoke.TABLE_NAME, values, updateSelection, null);
}
}
I'm getting an error on the update:
java.lang.RuntimeException: .... while compiling: UPDATE jokes SET used=?
WHERE setup=Why do programmers always mix up Halloween and Christmas?
It seems as though I'm not getting an actual value set for the used column. What the program ultimately does is cycle through jokes where used=0, then sets used to 1 when it has been viewed. So the query only pulls those jokes that aren't used yet. I have a feeling I'm missing something simple, one can hope.
I think you are having problems with quotation marks.
Example:
String updateSelection = JokeDbContract.TblJoke.COLUMN_NAME_SETUP + "=\"" + theSetup + "\"";
However, the recommended way to do this, would be:
theDb.update(JokeDbContract.TblJoke.TABLE_NAME, values, JokeDbContract.TblJoke.COLUMN_NAME_SETUP + " = ?", new String[] { theSetup });
It is better to use field = ?, because this helps sqlite cache queries (I believe).

notifyChange with changed uri from contentProvider.update()

i have implemented update() of ContentProvider and notifying to observer using getContext().getContentResolver().notifyChange(uri, null);
my obvious need is that whenever just one row is effected i want to notify with row specific uri, but could not find way to do so.
an additional query like "select id where selectionArgs" can do this but this will be a foolish way.
onchange(boolean, uri) get complete uri instead of specific row, easy to understand that this is because ContentProvider.update() is sending the same.
some code for more clarity
update() method of MyContentProvider
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.d("TAG", "update " + uri.getPath());
int count = 0;
switch (uriMatcher.match(uri)) {
case BOOKS:
count = booksDB.update(DATABASE_TABLE, values, selection, selectionArgs);
break;
case BOOK_ID:
count = booksDB.update(DATABASE_TABLE, values,
_ID + " = " + uri.getPathSegments().get(1)
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),
selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count == 1) {
Cursor c = query(uri, new String[] { _ID }, selection, selectionArgs, null);
long rowId = Long.valueOf(c.getString(c.getColumnIndex(_ID)));
uri = ContentUris.withAppendedId(CONTENT_URI, rowId);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
i will update table some how like
getContentResolver().update(MyContentProvider.CONTENT_URI, values1, MyContentProvider._ID+"<?", new String[]{"3"}));
frankly saying, code has barely related to question, just trying to give you some context
In your provider method, just return the uri with the id appended
#Override
public Uri insert(Uri uri, ContentValues values) {
Log.i(TAG, "insert " + uri);
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = URI_MATCHER.match(uri);
Uri returnUri;
switch (match) {
case MESSAGE: {
long _id = db.insert(MessageContract.MessageEntry.TABLE_NAME, null, values);
if (_id > 0)
returnUri = ContentUris.withAppendedId(MessageContract.MessageEntry.CONTENT_URI, _id);
else
throw new android.database.SQLException("Failed to insert row into " + uri);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
getContext().getContentResolver().notifyChange(returnUri, null);
return returnUri;
}
And register your observer with true for descendents.
getContentResolver().registerContentObserver(MessageContract.MessageEntry.CONTENT_URI, true, mContentObserver);
To get the id from a Uri you can use ContentUris.parseId(uri)
Unfortunately I'm not able to suggest easy solution (because I'm not aware of full code and updates You need to run), there's some ways we You could try (some of them I've implemented in mine applications):
Provide ids in ContentValues - this way looks not applicable for Your case and it needs loop with calls to notifyChange();
Provide specific Uri for requests with queries (only some specific apps needs many various queries in selection, usually it's much easier to include query parameter in Uri). After another part of the program get notification with that specific Uri it will be able to check if it's 'current item' was updated and act appropriately (e.g. simplest case with list of articles and one article open in separate activity; then You update list of articles in the background from server You might need to update currently open article also and so, need to know if it was updated). You should be able to check particular item on the side of the observer using just received Uri, because it (Uri) will contain parameter(s) You've used for query;
You can pass the ID via ContentValues, and append it to the notification url. This way you don't have to make a separate query.
#Override
public int update(#NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int rows = _database.update(getTableName(), values, selection, selectionArgs);
if (rows > 0) {
Uri itemUri = ContentUris.withAppendedId(uri, values.getAsLong(DatabaseModel.COLUMN_ID)); // DatabaseModel.COLUMN_ID is "_id"
getContext().getContentResolver().notifyChange(itemUri, null);
}
return rows;
}

Insert or update in SQlite and Android using the database.query();

is there a way to change my function:
public categorie createCategoria(String categoria) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_NOME, categoria);
values.put(MySQLiteHelper.COLUMN_PREF, 0);
long insertId = database.insert(MySQLiteHelper.TABLE_CATEGORIE, null,
values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_CATEGORIE,
allCategorieColumns, MySQLiteHelper.COLUMN_ID + " = " + insertId, null,
null, null, null);
cursor.moveToFirst();
categorie newCategoria = cursorToCategorie(cursor);
cursor.close();
return newCategoria;
}
this is a raw insert, i would like to change this function to make it update or insert accordingly. i would like to change this becouse i'm already using this function in some places, but now i need to choose if insert a row or update (or ignoring the insert) a row with the same COLUMN_NOME. can someone help me doing this?
i mean i would like to insert a new row ONLY if there isn't another with the same name (as usual you know).
You can use insertWithOnConflict() if you want to insert or update, depending in whether the record exists or not:
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(COLUMN_ID, id);
contentValues.put(COLUMN_VALUE, value);
// this will insert if record is new, update otherwise
db.insertWithOnConflict(TABLE, null, contentValues, SQLiteDatabase.CONFLICT_REPLACE);
you could call int nRowsEffected = database.update(...); if there are no rows effected by the update either the row doesn't exist (or you hosed your update()!) therefore you need to call database.insert(...). of course if nRowsEffected > 0 then you are done.
You can use execSQL and use INSERT OR REPLACE
String[] args = {"1", "newOrOldCategory"}; // where 1 is the category id
getWritableDatabase().execSQL("INSERT OR REPLACE INTO table_name (idColoumn, categoryColumn) VALUES (?, ?)", args);
First of all you have write function which is check whether id is exists in particular Table like:
/**
* #param table_name
* #param server_id
* #return
*/
public boolean isServerIdExist(String table_name, int server_id) {
long line = DatabaseUtils.longForQuery(mDB, "SELECT COUNT(*) FROM " + table_name + " WHERE id=?",
new String[]{Integer.toString(server_id)});
return line > 0;
}
You have to pass table_name and id in that like
/**
* INSERT in TABLE_ACCOUNT_DEVICE
**/
public long insertOrUpdateAccountDevice(int server_id, int account_id,
String device_name, String device_id,
String last_active, String itp,
String utp, int status) {
ContentValues values = new ContentValues();
values.put(ACCOUNT_DEVICE_ACCOUNT_ID, account_id);
values.put(ACCOUNT_DEVICE_DEVICE_NAME, device_name);
values.put(ACCOUNT_DEVICE_DEVICE_ID, device_id);
values.put(ACCOUNT_DEVICE_LAST_ACTIVE, last_active);
values.put(ACCOUNT_DEVICE_ITP, itp);
values.put(ACCOUNT_DEVICE_UTP, utp);
values.put(ACCOUNT_DEVICE_STATUS, status); // 0=pending, 1=active, 2=Inactive, -1=not_found
/**
* isServerIdExists
*/
if (isServerIdExists(TABLE_ACCOUNT_DEVICE, server_id)) {
values.put(ACCOUNT_DEVICE_SERVER_ID, server_id);
return mDB.insert(TABLE_ACCOUNT_DEVICE, null, values);
} else {
return mDB.update(TABLE_ACCOUNT_DEVICE, values, ACCOUNT_DEVICE_SERVER_ID + " =? ",
new String[]{Integer.toString(server_id)});
}
}
Hope it will helps you.

SQLite "database schema has changed" error in Content Provider

I'm using Content Providers and Sync Adapters for my synchronization routine.
My routine receives a JSONObject and insert or update the entry.
In order to decide if we are going to update or insert we check if the entry exists in the database. This is where the sqlite error occurs.
06-03 10:58:21.239: INFO/Database(340): sqlite returned: error code = 17, msg = prepared statement aborts at 45: [SELECT * FROM table WHERE (id = ?) ORDER BY id]
I have done some research and found this discussion about the subject. From this discussion I understand that sqlite_exec() has to be called. How would I implement this in a Content Provider?
Edit
Insert / Update check
// Update or Insert
ContentValues cv = new ContentValues();
/* put info from json into cv */
if(mContentResolver.update(ClientsProvider.CONTENT_URI, cv, null, null) == 0) {
// add remote id of entry
cv.put("rid", o.optInt("id"));
mContentResolver.insert(ClientsProvider.CONTENT_URI, cv);
}
ContentProvider::update
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
switch(uriMatcher.match(uri)) {
case CLIENTS:
count = clientDB.update(TABLE_NAME, values, selection, selectionArgs);
break;
case CLIENT_ID:
count = clientDB.update(TABLE_NAME, values, ID + " = " + uri.getPathSegments().get(0) + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
count = 0;
}
return count;
}
Problem is solved. I'm not sure why but after an emulator image wipe everything works exactly how its supposed to do. Thank you for your time Selvin!

Categories

Resources