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;
}
}
Related
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);
}
}
ok I just followed an instruction that I should do this to retrieve sql data from database but it just cuts to there so far I have this inside my databasehelper class.
public void getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
}
while (cursor.moveToNext());
}
db.close();
}
So somehow this does is it get all the values of my tables 4th column which contains an int... how do I retrieve the value in my MainActivity and save it in an array of integers?
just add everything in a ArrayList and return the arraylist
simply call the method in your main activty
public ArrayList<Integer> getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
ArrayList data= new ArrayList<>();
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
data.add(resource);
}
while (cursor.moveToNext());
}
db.close();
}
return data;
}
Well, as you have it, the variable resource is scoped only to the while loop. Even if it wasn't it would constantly get overwritten on each loop iteration.
Instead, you should declare a collection higher up and Add each value to it during your while loop. You could also redefine your function to return the collection if integers.
public List<int> getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
List<int> myVals = new List<int>();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource, null); //null for conditions
if (cursor.moveToFirst())
{
do
{
myVals.Add(cursor.getInt(3));
}
while (cursor.moveToNext());
}
db.close();
return myVals;
}
Also, as a note... string concatenation of a SQL query is a recipe for disaster. Look up SQL Injection and best practices to avoid it before continuing further. It is worth the time to get into good habits early on.
EDIT / ADDENDUM
Unless you also limit your result set returned from your table query, you will be getting every record. The function you have here really has no practical use and would likely cause more problems than any benefits it may have. I would suggest, as an example of a more usable function that returns a specific IconResource based on the IconId:
public int getIconResource(int iconId)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "select IconResource from IconTable where IconId = ?";
PreparedStatement pstmnt = db.prepareStatement(getresource);
pstrmnt.setString(1, iconId);
ResultSet rset = db.executeQuery();
int iconResource;
if (rset.next())
iconResource = rset.getInt("IconResource");
db.close();
return iconResource;
}
Of course, the above is making assumptions of your table structure.
Using the above, in your code elsewhere, you would simply call this function with the IconId and use the output however needed:
int iconResource = getIconResource(5); // returns the IconResource for IconId = 5
The above prevents any possible SQL Injection attacks by using a parameterized query and avoiding the use of dynamic concatenated strings sent to your SQL server.
You may try out the following code:
public List<Integer> getIconResource(String tblName)
{
List<Integer> list = new ArrayList<Integer>();
list.clear();
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
list.add(resource);
}
while (cursor.moveToNext());
}
db.close();
return list;
}
Then call this method in MainActivity and store the List in another Integer type list.
databasehelper dbhelper;
List<Integer> newList = dbhelper.getIconResource("Your tablename");
fot(int i = 0 ; i< newList.size() ; i++){
int yourValue = newList(i);
}
I have some trouble with a SQLite database with 1 table and 2 columns, column_id and word. I extended SQLiteAssetHelper as MyDatabase and made a constructor:
public MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
I need to check whether some string is in the database (in column word). I tried to modify the code from answer provided by Benjamin and dipali, but I used SQLiteAssetHelper and I can't get it to work. The method that I have in mind receives the string to search for as a parameter and returns a boolean if string is in the database.
public boolean someMethod(String s)
In addition, I tried to put the check on a background thread with AsyncTask because I have 60 strings to check.
TABLE_NAME and COLUMN_WORD should be self-explanatory.
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[] {COLUMN_WORD};
String where = COLUMN_WORD + " = ?";
String[] whereArgs = new String[] {s};
// select column_word from table where column_word = 's' limit 1;
Cursor cursor = db.query(TABLE_NAME, columns, where, whereArgs, null, null, null, "1");
if (cursor.moveToFirst()) {
return true; // a row was found
}
return false; // no row was found
}
You can do this in the background, but I don't think for a query like this it's even necessary.
EDIT
There are some improvements that should be made to the above for the sake of correctness. For one thing, the Cursor should be closed since it is no longer being used. A try-finally block will ensure this:
Cursor cursor = db.query(...);
try {
return cursor.moveToFirst();
} finally {
cursor.close();
}
However, this method doesn't need to obtain a whole `Cursor. You can write it as follows and it should be more performant:
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String sql = "select count(*) from " + TABLE_NAME + " where "
+ COLUMN_WORD + " = " + DatabaseUtils.sqlEscapeString(s);
SQLiteStatement statement = db.compileStatement(sql);
try {
return statement.simpleQueryForLong() > 0;
} finally {
statement.close();
}
}
You could add a catch block and return false if you think it's possible (and valid) to encounter certain exceptions like SQLiteDoneException. Also note the use of DatabaseUtils.sqlEscapeString() because s is now concatenated directly into the query string and thus we should be wary of SQL injection. (If you can guarantee that s is not malicious by the time it gets passed in as the method argument, then you could theoretically skip this, but I wouldn't.)
because of possible data leaks best solution via cursor:
Cursor cursor = null;
try {
cursor = .... some query (raw or not your choice)
return cursor.moveToNext();
} finally {
if (cursor != null) {
cursor.close();
}
}
1) From API KITKAT u can use resources try()
try (cursor = ...some query)
2) if u query against VARCHAR TYPE use '...' eg. COLUMN_NAME='string_to_search'
3) dont use moveToFirst() is used when you need to start iterating from beggining
4) avoid getCount() is expensive - it iterates over many records to count them. It doesn't return a stored variable. There may be some caching on a second call, but the first call doesn't know the answer until it is counted.
I am new to android development and have come across a problem with deleting a row in a SQLite table that I have not been able to find a solution to. When I try to delete a row nothing is happening.
In my activity, called 'MainActivity,' I am using a context menu to call a method to delete an item from the list. This portion of code is as follows:
case R.id.delete_program:
DBAdapter adap = new DBAdapter(this);
adap.open();
long pass = (long) (position + 1);
adap.removeFromCurrent(pass);
adap.close();
Runnable run = new Runnable() {
public void run(){
refreshCurrent(getApplicationContext());
}
};
runOnUiThread(run);
proAdapt.notifyDataSetChanged();
return true;
In 'DBAdapter' the method removeFromCurrent():
public boolean removeFromCurrent(long cp_id) {
return this.dB.delete(PROGRAMS_TABLE, CP_ID + '=' + cp_id, null) > 0;
}
In 'MainActivity' the method refreshCurrent():
public static void refreshCurrent(Context context) {
CURRENT.clear();
DBAdapter adap = new DBAdapter(context);
adap.open();
ArrayList<Program> temp = adap.getCurrentPrograms();
for(Program item : temp) {
Toast.makeText(context, "Item added: " + item.getName(), Toast.LENGTH_SHORT).show();
CURRENT.add(item);
}
adap.close();
}
I'm using the toast to check if the table has changed but the display has not.
From 'DBAdapter' the method getCurrentPrograms():
public ArrayList<Program> getCurrentPrograms() {
ArrayList<Program> list = new ArrayList<Program>();
Cursor cursor =
this.dB.query(PROGRAMS_TABLE, new String[] {
CP_ID, CP_NAME, ACTUAL_DAY, D_ID, CP_CYCLE, CP_DAY_ONE },
null, null, null, null, null);
int rows = cursor.getCount();
if(cursor != null)
cursor.moveToFirst();
for(int i = 1; i <= rows; i++) {
list.add(new Program(cursor.getString(cursor.getColumnIndex(CP_NAME)),
cursor.getInt(cursor.getColumnIndex(ACTUAL_DAY)),
cursor.getInt(cursor.getColumnIndex(CP_DAY_ONE)),
cursor.getInt(cursor.getColumnIndex(CP_CYCLE))));
cursor.moveToNext();
}
return list;
}
The ArrayList 'CURRENT' is the list used to populate the ListView. My thinking was that I would be able to delete a row from the table and then repopulate this list as well as the ListView. I was also curious as to what happened to a SQLite table once a row is removed; do the other rows move up a position, or do they stay in the same place?
I am still very new to this so any help about my problem or tips for the rest of my code would be much appreciated. Let me know what else I'm doing wrong with my programming.
If you are using the SQLiteOpenHelper you should be getting a SQLiteDatabase object and then using a transaction, if you don't set it as successful then nothing will happen
SQLiteDatabase db = mDbHelper.getWritableDatabase();
db.beginTransaction();
//Do stuff
db.setTrasactionSuccessfull();
db.endTransaction();
Also I think when you pass null into the where clause the where doesn't matter.
You should have something like db.delete("tbl", "id=?", new String[]{String.valueOf(id)});
I want to delete a row in the sqlite data base. Please refer to the sql datastructure and my current delete method which is not working.
private static final String[] TABLE_MESSAGE_FIELDS = {
FIELD_USERNAME, "CHAR(32) NOT NULL",
FIELD_READED, "INTEGER(1) NOT NULL",
FIELD_SEND, "INTEGER(1) NOT NULL",
FIELD_TIMESTAMP, "INTEGER(64) NOT NULL",
FIELD_CONTENT, "TEXT NOT NULL",
};
//more tables
private static final String[] TABLE_MESSAGE_INDEXS = {
FIELD_USERNAME, FIELD_READED,
};
This is the structure, basically it is an instant messenger (IM) android app, so the user can send and receive message. While the operations like receiving message etc are working, the option to delete is not.
I am looking to delete the whole conversation between a user, in other words not the individual message in a conversation, but the whole conversation itself. I guess the right way is to find out the user name, and delete the entire row. The table TABLE_MESSAGE_FIELDS is contains the 5 columns indicating the message, I want to delete that entire conversation.
This is how I go about it
public boolean deleteMessage(String userName)
{
SQLiteDatabase database = mLocalDatabase.getWritableDatabase();
final String[] COLUMNS = { FIELD_TIMESTAMP, FIELD_CONTENT };
final String SELECTION = FIELD_USERNAME + "=?" ;
//database.beginTransaction();//do i need this?
boolean result= database.delete(TABLE_MESSAGE,SELECTION,new String[]{userName})>=0;
//database.endTransaction();//??
database.close();
return result;
}
Assuming you have correctly declared create query and everything works, your code looks correct so reason why your row(s) are not deleted from database may be that you provided wrong username i.e. each row in database not equal with given userName. Try to print your variable before perform delete action.
Then, you mentioned transaction. If you used it, you need to call
setTransactionSuccessful()
or your changes will be rolled back and database will be back to state before transaction.
boolean result = false;
db.beginTransaction();
result = db.delete(TABLE_MESSAGE, SELECTION, new String[] {userName}) > 0;
db.setTransactionSuccessful();
db.endTransaction();
public boolean deleteMessage(String userName) {
boolean result = false;
if (userName != null && !userName.equals("")) {
userName = userName.trim();
SQLiteDatabase database = mLocalDatabase.getWritableDatabase();
// final String[] COLUMNS = { FIELD_TIMESTAMP, FIELD_CONTENT };
// final String SELECTION = FIELD_USERNAME + "=?" ;
// database.beginTransaction();//do i need this?
String whereCondition = FIELD_USERNAME + "=" + userName;
result = database.delete(TABLE_MESSAGE, whereCondition, null);
// database.endTransaction();//??
database.close();
}
return result;
}