I haven't found a good example for updating all data in your SQLite database.
I have a model class called ItemDatabaseModel that contains values such as item_id, category, description, image_url, etc. When updating a list of these ItemDatabaseModels, I am currently updating every attribute one at a time but the end result is that all the data is overwritten with the last item that was updated.
Here is how I am doing my update. I start by iterating through my list of ItemDatabaseModels that I want to update.
for (int i = 0; i < alItemDb.size(); i++) {
dbProvider.update(alItemDb.get(i));
}
The update method that is being called accepts the model and then calls the update method for updating the database.
public void update(ItemDatabaseModel itemDatabaseModel) {
mProvider.update(itemDatabaseModel, ItemSchema.CATEGORY + " =?", new String[]{itemDatabaseModel.category});
mProvider.update(itemDatabaseModel, ItemSchema.ITEM_ID + " =?", new String[]{itemDatabaseModel.itemId});
mProvider.update(itemDatabaseModel, ItemSchema.DESCRIPTION + " =?", new String[]{itemDatabaseModel.description});
mProvider.update(itemDatabaseModel, ItemSchema.IMAGE_URL_1 + " =?", new String[]{itemDatabaseModel.imageUrl1});
mProvider.update(itemDatabaseModel, ItemSchema.IMAGE_URL_2 + " =?", new String[]{itemDatabaseModel.imageUrl2});
mProvider.update(itemDatabaseModel, ItemSchema.IMAGE_URL_3 + " =?", new String[]{itemDatabaseModel.imageUrl3});
}
Then I update the database:
public void update(T model, String where, String[] whereArgs) {
mDatabase = getWritableDatabase();
if (mDatabase.isOpen()) {
mDatabase.update(model.getTableName(), model.getContentValues(), where, whereArgs);
}
}
From my observation the issue could be the where, but I do not know how to adjust it. Also, is there a better implementation for updating a list of new data?
For those of you wondering what the necessary corrections were to resolve this issue, here are the final changes.
Iterate through your list of items to udpate
for (int i = 0; i < alItemDb.size(); i++) {
dbProvider.update(alItemDb.get(i));
}
Pass in your entire model so that you can later retrieve contentValues from it, and perform your update based upon an unique identifier (id, email, ect)
public void update(ItemDatabaseModel itemDatabaseModel) {
mProvider.update(itemDatabaseModel, ItemSchema.ITEM_ID + " = ?",
}
Update your database
public void update(T model, String where, String[] whereArgs) {
mDatabase = getWritableDatabase();
if (mDatabase.isOpen()) {
mDatabase.update(model.getTableName(), model.getContentValues(), where, whereArgs);
}
}
Related
i have a table names "highscore"
In the table there is:
id (int) ,
name (string) ,
win(int) ,
draw(int),
loss(int).
I want to make a query that i can get the specific value win from the row , only the integer.. how can i do that? i want to handle sql injection to.
I have a method that update the win, but i need to get the win, increment the value with 1 and then update. My update method is this and it works:
public void updateWin(String playerName, int win) {
SQLiteDatabase db = this.getReadableDatabase();
ContentValues values = new ContentValues();
values.put(Constants.KEY_WIN, win);
db.update(Constants.TABLE_NAME, values, Constants.KEY_PLAYER_NAME + "= ?", new String[]{playerName});
db.close();
}
Anyone can help me please? thanx
Option 1 - Increment according to arithmetic calculation within SQL
You could base the this on the SQL (assuming the table is mytable001 and the player's name is FRED) :-
UPDATE mytable001 SET win = win +1 WHERE playername = 'FRED';
This would do away with the need to query the playername to get the current number of wins as it directly increments the value.
However, this cannot be done via the convenience update method nor a rawQuery you have utilise execSQL.
So the following could be used :-
public boolean incrementWin(String playerName) {
SQLiteDatabase db = this.getWritableDatabase();
String esc_playername = DatabaseUtils.sqlEscapeString(playerName);
String qrysql = "UPDATE " +
Constants.TABLE_NAME +
" SET " +
Constants.KEY_WIN + " = " +
Constants.KEY_WIN + " + 1" +
" WHERE " +
Constants.KEY_PLAYER_NAME + "=" + esc_playername;
db.execSQL(qrysql);
long changes = DatabaseUtils.longForQuery(db,"SELECT changes()",null);
db.close();
return changes > 0;
}
Note if the update couldn't be/ wasn't performed then it would return false.
The use of sqlEscapeString, will escape the playername and I believe offer some protection against SQL Injection.
Option 2 - Retrieve current value, calculate new, update using new :-
public boolean incWin(String playername) {
SQLiteDatabase db = this.getWritableDatabase();
String whereclause = Constants.KEY_PLAYER_NAME + "=?";
String[] wherargs = new String[]{playername};
int win = -1; // default to not update
Cursor csr = db.query(
Constants.TABLE_NAME,
null,
whereclause,
wherargs,
null,
null,
null
);
if (csr.moveToFirst()) {
win = csr.getInt(csr.getColumnIndex(Constants.KEY_WIN)) + 1;
}
csr.close();
if (win < 1) {
db.close();
return false;
}
ContentValues cv = new ContentValues();
cv.put(Constants.KEY_WIN,win);
if (db.update(Constants.TABLE_NAME,cv,whereclause,wherargs) > 0) {
db.close();
return true;
}
db.close();
return false;
}
Note if the update couldn't be/ wasn't performed then it would return false.
I am pretty sure my sqlite syntax is right but I don't know why when database result is loaded in my listview it show all of my data. I think the problem is about my cursor snippet.
How can I fix this problem that load only rows where table_one.id = table_two.day_id
#Override
public List<Model> getAllEx() {
SQLiteDatabase db = helper.getWritableDatabase();
List<Model> list = new ArrayList<>();
String query = "SELECT * FROM " + Constants.TABLE_ONE + " INNER JOIN " + Constants.TABLE_TWO +" ON "+
Constants.ID_ONE + " = " + Constants.ID_DAY;
Cursor c = db.rawQuery(query, null);
while (c.moveToNext()) {
Model model = new Model();
model.setSecond_id(c.getInt(c.getColumnIndex(Constants.ID_TWO)));
model.setExercise(c.getString(c.getColumnIndex(Constants.EXERSICE)));
model.setNumber(c.getString(c.getColumnIndex(Constants.NUMBER)));
model.setReps(c.getString(c.getColumnIndex(Constants.REPS)));
model.setId_day(c.getInt(c.getColumnIndex(Constants.ID_DAY)));
list.add(model);
}
return list;
}
I am not sure what values you have in your constants but is your query like this
select * from table_one as one inner join table_two as two
on one.id = two.day_id
where one.category="cat_one"
and two.category="cat_two"
You can remove the category of second/first table if not required.
I have a listview that displays a list of items that are stored in a database. I can display and edit these items perfectly fine.
The items can also be deleted fine, but the problem comes when i try to readd items after things have been deleted. Sometimes the error doesn't occur depending on how you delete things and I can't work out what is going on.
an example of the error:
I will have 3 items: a, b, c
If i delete just one of those items from any position in the list then readd it is fine, in any combination. However, if i was to delete b and c then tried to readd b, i would have a list of a, b, b. with a duplicate of b showing that crashes if selected. the first b being deleted will also delete it.
if i delete a, b and c and try and read a then i will get a list of a, a with another duplicate showing. If i was then to add b to the list i would get a, b, a, b with the same problems as before. if i keep deleting things from this point eventually when i try and readd an item i will get more and more duplicates appearing in the listview.
It's as if as many items as i delete from the list, it will then add that many duplicate items onto the end of the list to fill it up. Unless i only delete one item at a time.
I'm not sure what code to put in here. This isn't a problem with just displaying things wrong. The database is returning a list wth these duplicates in. Curious if someone might have a general idea what is going on. I'm not sure what code to post. My code is based on the tutorial found here - http://www.androidhive.info/2013/09/android-sqlite-database-with-multiple-tables/
public long createToDo(Todo todo, long[] tag_ids) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_TODO, todo.getNote());
values.put(KEY_STATUS, todo.getStatus());
values.put(KEY_PRICE, todo.getPrice());
values.put(KEY_CREATED_AT, getDateTime());
values.put(KEY_PURCHASED, todo.getPurchased());
// insert row
long todo_id = db.insert(TABLE_TODO, null, values);
// insert tag_ids
for (long tag_id : tag_ids) {
createTodoTag(todo_id, tag_id);
}
return todo_id;
}
to delete the returned todo_id is sent into this delete method
public void deleteToDo(long tado_id) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_TODO, KEY_ID + " = ?",
new String[] { String.valueOf(tado_id) });
}
here is the function i use to fetch the todos for each list
public List<Todo> getAllToDosByTag(String tag_name) {
List<Todo> todos = new ArrayList<Todo>();
String selectQuery = "SELECT * FROM " + TABLE_TODO + " td, "
+ TABLE_TAG + " tg, " + TABLE_TODO_TAG + " tt WHERE tg."
+ KEY_TAG_NAME + " = '" + tag_name + "'" + " AND tg." + KEY_ID
+ " = " + "tt." + KEY_TAG_ID + " AND td." + KEY_ID + " = "
+ "tt." + KEY_TODO_ID;
Log.e(LOG, selectQuery);
SQLiteDatabase db = this.getReadableDatabase();
Cursor c = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (c.moveToFirst()) {
do {
Todo td = new Todo();
td.setId(c.getInt((c.getColumnIndex(KEY_ID))));
td.setNote((c.getString(c.getColumnIndex(KEY_TODO))));
td.setCreatedAt(c.getString(c.getColumnIndex(KEY_CREATED_AT)));
td.setStatus(c.getInt((c.getColumnIndex(KEY_STATUS))));
td.setPrice(c.getInt((c.getColumnIndex(KEY_PRICE))));
td.setPurchased(c.getInt((c.getColumnIndex(KEY_PURCHASED))));
// adding to todo list
todos.add(td);
} while (c.moveToNext());
}
return todos;
}
and here's how i am displaying them in my other activity
listOfTodos = db.getAllToDosByTag(nameOfList);
final ListView lv1 = (ListView) findViewById(R.id.listItems);
lv1.setAdapter(new MyCustomBaseAdapter(this, listOfTodos);
-
public long createTodoTag(long todo_id, long tag_id) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_TODO_ID, todo_id);
values.put(KEY_TAG_ID, tag_id);
values.put(KEY_CREATED_AT, getDateTime());
long id = db.insert(TABLE_TODO_TAG, null, values);
return id;
}
i'm creating a contentProvider , and i wish to be able to send it multiple DB records (contentValues) to be inserted or updated to a single table using a single batch operations .
how do i do that?
batchInsert is intended only for inserting , but wouldn't it mean that insertion of something that already exists won't do anything?
also , is there a way for the update operation to use a special constraint ? for example , i need to ignore the primary key and update based on 2 other fields that together are unique.
"batchInsert is intended only for inserting" : this is true BUT you can override it in your ContentProvider to perform an UPSERT (insert/update) depending on the URI passed to batchInsert.
The following is some working code that I currently use to perform bulk inserts on time-series data (admittedly, I just delete anything that gets in the way instead of updating, but you could easily change this to your own ends.).
Also note the use of the sql transaction; this speeds up the process immensely.
#Override
public int bulkInsert(Uri uri, ContentValues[] values) {
SQLiteDatabase sqlDB = database.getWritableDatabase();
switch (match(uri)) {
case ONEPROGRAMME:
String cid = uri.getLastPathSegment();
int insertCount = 0;
int len = values.length;
if (len > 0) {
long start = values[0].getAsLong(Programme.COLUMN_START);
long end = values[len - 1].getAsLong(Programme.COLUMN_END);
String where = Programme.COLUMN_CHANNEL + "=? AND " + Programme.COLUMN_START + ">=? AND "
+ Programme.COLUMN_END + "<=?";
String[] args = { cid, Long.toString(start), Long.toString(end) };
//TODO use a compiled statement ?
//SQLiteStatement stmt = sqlDB.compileStatement(INSERT)
sqlDB.beginTransaction();
try {
sqlDB.delete(tableName(PROGRAMME_TABLE), where, args);
for (ContentValues row : values) {
if (sqlDB.insert(tableName(PROGRAMME_TABLE), null, row) != -1L) {
insertCount++;
}
}
sqlDB.setTransactionSuccessful();
} finally {
sqlDB.endTransaction();
}
}
if (insertCount > 0)
getContext().getContentResolver().notifyChange(Resolver.PROGRAMME.uri, null);
return insertCount;
default:
throw new UnsupportedOperationException("Unsupported URI: " + uri);
}
}
i'm working on a android app that will display Strings to the user, and the user then has the option to add one to a favorite list. I have searched and searched and cannot find the proper way of doing this. I did get one open source project, everything worked until the user removed a favorite. The database would clear the row of data, but when a new row is added, it would behave as if the deleted row still had data, leaving blanks in the favorite list.
this is my insert method
public long insertString(String newString)
{
ContentValues newStringValue = new ContentValues();
newStringValue.put(KEY_STRING, newString);
return db.insert(DATABASE_TABLE, null, newStringValue);
}
the long returned will always increment even if i use the remove method:
public boolean removeString(long _rowIndex)
{
return db.delete(DATABASE_TABLE, KEY_ID + "=" + _rowIndex, null) > 0;
}
if i try to remove the third index, and the user removed a question at the third index, the method returns false, is there a way to completely remove all rows with no data?
You should use a CursorAdapter or ResourceCursorAdapter and when you modify the data call cursor.requery() to refresh everything.
Maybe just encode String List as something like JSON, then save as long string (blob / clob)? I would use Jackson JSON processor, but there are many alternatives to choose from (Guice, or XStream if you prefer XML). I mean, assuming you don't really need relational aspects of data (no need to find users with specific list entry by querying) but just need to persist lists.
public static class OrderManager
{
private MyDBOpenHelper _db_Orders;
private static final String GET_Orders = “SELECT * FROM “+Order_TABLE_NAME ;
public OrderManager(Context context)
{
_db_Orders = new MyDBOpenHelper(context);
}
public boolean insert(String orderName, String orderStatus)
{
try
{
SQLiteDatabase sqlite = _db_Orders.getWritableDatabase();
/*
sqlite.execSQL(“INSERT INTO “+ Order_TABLE_NAME +
” (” + KEY_NAME +”, “+ KEY_STATUS + “)” +
” VALUES (‘” + orderName + “‘, ‘” + orderStatus + “‘)”);
*/
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_NAME, orderName);
initialValues.put(KEY_STATUS, orderStatus);
sqlite.insert(Order_TABLE_NAME, null, initialValues);
}
catch(SQLException sqlerror)
{
Log.v(“Insert ERROR”, sqlerror.getMessage());
return false;
}
return true;
}
public ArrayList<Order> getOrders()
{
ArrayList<Order> orders = new ArrayList<Order>();
SQLiteDatabase sqliteDB = _db_Orders.getReadableDatabase();
Cursor crsr = sqliteDB.rawQuery(GET_Orders, null);
Log.v(“Select Query result”, String.valueOf(crsr.getCount()) );
crsr.moveToFirst();
for(int i=0; i < crsr.getCount(); i++)
{
orders.add(new Order(crsr.getString(0), crsr.getString(1)));
//Log.v(“DATA”, crsr.getString(0) + ” ” +crsr.getString(1));
crsr.moveToNext();
}
return orders;
}
}