Android database bug when deleting items then trying to add more - android

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

Related

Get a specific value from a specific column from specific row?

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.

How to insert to into many to many relationships. SQLite

In my android app I have an SQLite database. with this structure:
PrivateList(idList INTEGER PRIMARY KEY, name TEXT, creationDate TEXT, active INTEGER, deactivationDate TEXT);
PrivateProduct (idProduct INTEGER PRIMARY KEY, description TEXT, quantity INTEGER, active INTEGER, additionDate TEXT);
List_Product(idList INTEGER NOT NULL, idProduct INTEGER NOT NULL, PRIMARY KEY (idList, idProduct), FOREIGN KEY(idList) REFERENCES PrivateList(idList), FOREIGN KEY(idProduct) REFERENCES PrivateProduct(idProduct));
I have an autogenerator list and elements using for to try the app:
localDB = new LocalDB(this, "localBD", null, 1);
SQLiteDatabase sqLiteDatabase = localDB.getWritableDatabase();
if (sqLiteDatabase != null){
for (int i = 0; i < 10; i++){
String a = "List" + i;
String b = "Product" + i;
Log.i("execfor", "INSERT INTO PrivateList (name, creationDate, active, deactivationDate) " + " VALUES ('" + a + "', CURRENT_TIMESTAMP, 1, null);");
sqLiteDatabase.execSQL("INSERT INTO PrivateList (name, creationDate, active, deactivationDate) " + " VALUES ('" + a + "', CURRENT_TIMESTAMP, 1, null);");
sqLiteDatabase.execSQL("INSERT INTO PrivateProduct (description, quantity, active, additionDate) " + " VALUES ('" + b + "', 3, 1, CURRENT_TIMESTAMP);");
//sqLiteDatabase.execSQL("INSERT INTO List_Product (idList, idProduct) values ");//
}
}
But I can`t find the way to get rowIds from each list and product to insert both, idlist and idproduct, into List_Product.
Thank you in advance.
The main change to facilitate grabbing the id's would be to swap from using execSQL to using insert as insert returns the id of the inserted row, execsql does not.
A little more on this here Using execSQL for INSERT operation in Android SQLite.
However, I'm not sure if you can pass CURRENT_TIMESTAMP via a ContentValues and it would result getting the current timestamp as opposed to just setting the value to the literal CURRENT_TIMESTAMP. You could use DEFAULT CURRENT_TIMEPSTAMP in the respective column definitions (as I have in the code below).
I'd suggest that you would not want a link between every list/product permutation (that would be 100 rows for you 10 List rows and 10 Product rows) as in real life you would probably not have such a scenario rather you'd have some links between the two. So in the code below I've randomly created links.
First some code from the Database Helper (for my convenience named SO45449914) for performing the inserts:-
public long insertListRow(String name,
int active) {
ContentValues cv = new ContentValues();
cv.put(LISTNAME_COL,name);
cv.put(LISTACTIVE_COL,active);
cv.put(LISTDEACTIVATIONDATE_COL,"");
return this.getWritableDatabase().insert(PRIVATELISTTABLE,null,cv);
}
public long insertProductRow(String description,int quantity, int active) {
ContentValues cv = new ContentValues();
cv.put(PRODUCTDESCRIPTION_COL,description);
cv.put(PRODUCTQUANTITY_COL,quantity);
cv.put(PRODUCTACTIVE_COL,active);
return this.getWritableDatabase().insert(PRIVATEPRODUCTTABLE,null,cv);
}
public void insertListProductLink(long listid, long productid) {
ContentValues cv = new ContentValues();
cv.put(LISTPRODUCTLIST_COL,listid);
cv.put(LISTPRODUCTPRODUCT_COL,productid);
if (this.getWritableDatabase().insertOrThrow(LISTPRODUCTTABLE,null,cv) <0) {
//handle failed insert
}
}
Notes
- I've used class variables for all columns names.
- Columns that have the current time stamp get this via the default, so there is no need to have a cv.put for those columns.
In the activity is the following code :-
void doSO45449914() {
SO45449914 dbhelper = new SO45449914(this);
int loopcount = 10;
long[] listids = new long[loopcount];
long[] productids = new long [loopcount];
for (int i=0; i < 10; i++) {
listids[i] = dbhelper.insertListRow("a" + i,1);
productids[i] = dbhelper.insertProductRow("b" + i,3,1);
}
Cursor csra = dbhelper.getWritableDatabase().query(SO45449914.PRIVATELISTTABLE,
null,null,null,null,null,null
);
Cursor csrb = dbhelper.getWritableDatabase().query(SO45449914.PRIVATEPRODUCTTABLE,
null,null,null,null,null,null
);
Log.d("SO45449914","Number of rows in LIST TABLE = " + csra.getCount());
Log.d("SO45449914","Number of rows in PRODUCTS TABLE = " + csrb.getCount());
for (long aid: listids) {
Log.d("SO45449914","LIST ID from store = " + Long.toString(aid));
}
for (long bid: productids) {
Log.d("SO45449914","PRODUCT ID from store = " + Long.toString(bid));
}
for (long lid: listids) {
for (long prdid: productids) {
if ((Math.random() * 100) > 60) {
dbhelper.insertListProductLink(lid,prdid);
Log.d("SO45449914",
"Adding link between List id(" +
Long.toString(lid) +
") and product id(" +
Long.toString(prdid) +
")"
);
}
}
}
csra.close();
csrb.close();
}
Exlapnation
The first few lines prepare long arrays based upon the number of Lists and products to be created (same number of both). Integer loopcount determines how many.
The first loop, inserts Lists and Products which use the insert method storing the returned id in the respective array element.
Two Cursors are then created for obtaining row counts, which are then written to the log. The id's as stored in the arrays are output to the log.
Two nested loops are then invoked with Products being the inner (not that it matters) and randomly (about 40% of the time) a row will be inserted into the link table. I've assumed random but you always easily adjust the algorithm to follow a pattern. It's if ((Math.random() * 100) > 60) { that determines whether or not to insert a link.
The two Cursors are then closed.
Results
Here are screen shots of the resultant tables :-
PrivateList Table
PrivateProduct Table
List_Product Table
..... (44 rows in the List_Product table)
Well, this is what I did. Despite of the fact that there is a way do the same without so many rows in List_Product table; I'd like to understand the way. (Also I had problem in the for so it didnt do what I wanted exactly).
localDB = new LocalDB(this, "localBD", null, 1);
SQLiteDatabase sqLiteDatabase = localDB.getWritableDatabase();
if (sqLiteDatabase != null){
long idList;
long idProduct;
for (int i = 0; i < 10; i++){
String a = "List" + i;
String b = "Product" + i;
ContentValues contentValuesList = new ContentValues();
contentValuesList.put("name", a);
contentValuesList.put("active", 1);
contentValuesList.put("creationDate", "CreationDate");
contentValuesList.put("deactivationDate", "");
idList = sqLiteDatabase.insert("PrivateList", null, contentValuesList);
for (int j = 0; j < 10; j++){
ContentValues contentValuesProduct = new ContentValues();
contentValuesProduct.put("description", b);
contentValuesProduct.put("active", 1);
contentValuesProduct.put("quantity", 1);
contentValuesProduct.put("additionDate", "additionDdate");
idProduct = sqLiteDatabase.insert("PrivateProduct", null, contentValuesProduct);
ContentValues contentValuesListProduct = new ContentValues();
contentValuesListProduct.put("idList", idList);
contentValuesListProduct.put("idProduct", idProduct);
sqLiteDatabase.insert("List_Product", null, contentValuesListProduct);
}
I know it could be more efficient, but that it doesn't matter now.
This is the result in the database:
PrivateList:
with 10 rows
PrivateProduct:
with 100 rows.
List_Product:
The problem was that I didn't know the existance of sqlLiteDatabase.insert(...) method.
Thanks you all.

my database loads all of my data in listview every time database called

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.

passing parameter of _id and updating SQLitedatabase

I'm trying to update an SQLite table using the row id as my where statement. I'm getting the row _id from a row shown in a listview and passing to another activity with this statement:
Cursor cursor = (Cursor) nurseTableAdapter.getItem((int)id);
showAssignments.putExtra("Nurse", cursor.getInt(cursor.getColumnIndex("_id")));
The receiving activity receives the parameter:
nurse = extras.getString("Nurse");
and passes it as a parameter to my DbCommunicator class:
updateAssignments.updateNurseAssignments(listItemsArray, nurse);
and my DbCommunicator class does this with it:
public void updateNurseAssignments(String[] choices, String nurseId) {
// set parameter variables
//int nurseIdToInt = Integer.parseInt(nurseId);
//String strFilter = "_id=" + nurseIdToInt;
//String where = "_id=?";
String[] whereArgs = {String.valueOf(nurseId)};
Log.i(TAG, "value of whereArgs is " + whereArgs[0]);
// set content values
ContentValues cvUpdate = new ContentValues();
cvUpdate.put(KEY_FIRST_ASSIGNMENT, choices[0]);
cvUpdate.put(KEY_SECOND_ASSIGNMENT, choices[1]);
cvUpdate.put(KEY_THIRD_ASSIGNMENT, choices[2]);
cvUpdate.put(KEY_FOURTH_ASSIGNMENT, choices[3]);
cvUpdate.put(KEY_FIFTH_ASSIGNMENT, choices[4]);
cvUpdate.put(KEY_SIXTH_ASSIGNMENT, choices[5]);
// update database
sqldb.update(NURSE_TABLE, cvUpdate, KEY_NURSE_ROWID + "= ?", whereArgs);
}
I'm getting no errors, but the table is not updating. I've only found one similar example in Stack Overflow, and have tried to incorporate some of that here, but still having problems. Suggestions appreciated.

Android: SQLite, small issue with inserting and query

I am developing my first application which contains an SQLite database, containing table ANIMAL which has the columns _id, animal_name, animal_bio.
The user is presented with animal names in a ListView; selecting an animal will bring him/her to a page where he/she can view the animals bio.
The problem I'm having is:
When I add the bio of each animal to the DB - no errors.
However, running the app causes the ListView (previously working) to display a blank screen.
My Insertion code:
public long populateDB(){
ContentValues initialValues = new ContentValues();
long[] rowIds = new long[animalName.length];
// Populate the animal table
for(int i = 0; i < animalName.length; i++){
initialValues.put(KEY_ANIMALNAME, animalName[i]);
initialValues.put(KEY_BIOGRAPHY, bio[i]);
rowIds[i] = qmDB.insert(ANIMAL_TABLE, null, initialValues);
}
return rowIds[0];
}
And the create database statement
private static final String DATABASE_CREATE =
"create table " + ANIMAL_TABLE +
" (_id integer primary key autoincrement, " +
"animal_name text not null, " +
"biography text not null);";
I cannot see anything wrong with this code, so if anyone has any suggestions, I'd be very grateful.
EDIT: Retrieving animals code
public Cursor retrieveAnnimals(){
return qmDB.query(ANIMAL_TABLE, new String[] {
KEY_ROWID,
KEY_ANIMALNAME,
},
null,
null,
null,
null,
ORDER_BY_NAME);
}
Calling the create and insert from application, this takes place in the ListActivity, called ListAnimals:
dbm = new MyDBManager();
dbm.open();
dbm.deleteTable();
dbm.populateTable();
myCursor = dbm.retrieveAnimals();

Categories

Resources