Odd database error: Empty values? - android

I'm creating an activity that views a record and displays it's data to a user to modify. After everything is done just click a button to save the data back into the database. However, once saving time comes around I get an unusual "java.lang.IllegalArgumentException: Empty values" error. I think it's a SQL code error on my end, but I don't know enough about SQL to find it. Can I get a couple of eyes to see what I did wrong?
Thanks
~Aedon
Here is where the save button is declared and the listener is set. mBoundService is the service that hosts the database and it's calls. DevicesTable is a class that holds the table and column names. DevicesTable.gt_fields[0] is autoincrementID and then serial, name, and first seen columns
public void init() {
mDevName = (EditText)findViewById(R.id.dv_name);
mCurReads = (TextView)findViewById(R.id.dv_readings);
mSave = (Button)findViewById(R.id.dv_save);
mSave.setOnClickListener(new OnClickListener() {
#Override public void onClick(View arg0) {
mBoundService.updateRecord(DevicesTable.gt_name, mId, DevicesTable.g_fields[2], mDevName.getText().toString());
}
});
}
And this is the database call itself.
/**
* Updates a record at the given table with the given record.
* #param table The table to update
* #param id The id of the actual record. Do not pass the incorrect id!
* #param column The column in which the data is changing
* #param data The data that is to be changed to
*/
public void updateRecord(String table, String id, String column, String data) {
mDB.update(table, null, "SET " + column + " = '" + data + "',", new String[]{"id=" + id});
}

You need to pass in a ContentValues object to tell the database what values go in what columns in the updated row(s). The strings in the third and fourth arguments make up a WHERE clause that indicate what row(s) should be updated. For example:
ContentValues values = new ContentValues();
values.put("foo", 123);
values.put("bar", 456);
db.update("some_table", values, "id=789", null);
This would be equivalent to issuing the query
UPDATE some_table SET foo = 123, bar = 456 WHERE id = 789
Your code attempts to stuff the entire query into the WHERE clause, and update() stops you in your tracks because the values argument is null.

Related

How to fetch user details from a SQLite database having two tables?

I am trying to fetch the user details in my Navigation drawer. I have two different tables, one has username and course already stored and the other has id and email of the user taken during registration. I want to display the user name, id, course and email. I do have a user class with id, email and password for registering but not name and course. How should i fetch data from this class?
The below method is for fetching the username from table having same id. I want to display this on navigation drawer. How should i call the function and display the result?
public String getUserNameFromSID(String sid) {
String rv = "";
String whereclause = KEY_SID + "=?";
String[] whereargs = new String[]{sid};
SQLiteDatabase db = this.getReadableDatabase();
Cursor csr = db.query(TABLE_USERINFO,null,whereclause,whereargs,null,null,null);
if (csr.moveToFirst()) {
rv = csr.getString(csr.getColumnIndex(KEY_NAME));
}
csr.close();
return rv;
You can use a JOIN, (I believe from previous questions that SID is in both tables so this is what you would use as the basis for the JOIN).
So you could use something like SELECT * FROM userinfo JOIN userreg ON userinfo.sid = userreg.sid
Note that just using the column name sid would be ambiguous and hence the need to prefix the column with the table name. As it stands you will have to sid columns in the result (this shouldn't be an issue as they will both be the same value but not specifying an alias using AS cab be problematic for columns with the same name).
To utilise the query convenience method the JOIN needs to be part of the 1st (table) parameter.
So the above would be coded as :-
public String getUserNameFromSID(String sid) {
String rv = "";
String joinclause = " JOIN " + TABLE_USERREG + " ON " + TABLE_USERINFO + "." + KEY_SID + " = " + TABLE_USERREG + "." + KEY_SID; //<<<<<<<<<< ADDED
String whereclause = KEY_SID + "=?";
String[] whereargs = new String[]{sid};
SQLiteDatabase db = this.getReadableDatabase();
Cursor csr = db.query(TABLE_USERINFO + joinclause,null,whereclause,whereargs,null,null,null); //<<<<<<<<<< CHANGED
if (csr.moveToFirst()) {
rv = csr.getString(csr.getColumnIndex(KEY_NAME));
}
csr.close();
return rv;
}
Note the above is in-principle code as such
variable names and identifiers may not reflect the actual code and thus may need to be changed.
the code has not been run or tested and may therefore contain some minor errors.
The above only returns the username, you cannot return multiple distinct values, you would therefore need to return an object e.g. a User object to encompass the multiple values. You would get the values to set the object in a similar way as rv is set above.
Additional re comment :-
The thing is, that i would need to pass the Sid parameter to the
function which i can only do in the register/login page. How then can
i pass this value to the navigation page?
Instead of pages the term used for Android is Activities. You can pass data from one activity to another called/started/invoked activity using Intent Extras after instantiating an Intent in preparation for the call. That is if the activity is to be directly called from the parent activity (registration activity in your case).
If for example the registration activity were called from an initial activity and returns from that activity then you still use an intent to return the values, but the registration activity should be called/started/invoked using startActivityForResult. The activity starting the registration activity should override the onActivityResult method. The registration activity sets intent extras and uses setResult to indicate the result code and then intent to be returned.
There's plenty of examples on Stack overflow e.g. Sending data back to the Main Activity in Android

Creating a SQLite database for the game Tic Tac toe and connecting it to the main Activity [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I want to create a SQLite database for the game Tic Tac Toe on Android Studio that has the names and score of the players. Every time a user win, a point is added on the scoreboard.
How would I do this?
Stage 1 - Database Design
First design the database, you have identified Data as User name, and score, assuming you don't want historical data then a single table would suffice.
So design would be a table, perhaps called scoreboard, with columns :-
username
score and
To perhaps aid future changes a column name id that uniquely identifies a user (e.g. say you had two Toms or even two Tom smiths), this identifier (which is generally available) will be an alias of rowid. As Cursor Adapters require the id to be named _id then that will be used.
As such you could have a table that is created using the following SQL :-
CREATE TABLE IF NOT EXISTS scoreboard
(
_id INTEGER PRIMARY KEY,
username TEXT,
score INTEGER
);
Stage 2 - Creating the Database
When getting started with SQLite for Android it is probably best to utilise a subclass of SQLiteOpenHelper as what many refer to as the DBHelper.
So create a class say DBHelper.java which extends the SQLiteOpenHelper class.
Note you must include overrides for the onCreate method and the onUpgrade method.
If using Android Studio when adding a new class;
input, DBHelper in the **Name* field,
type SQLiteOpenHelper in the Superclass field (by the time you've typed SQL you will see SQLiteOpenHelper double click is) and
then tick/check the Show Select Overrides Dialog.
Click OK
You will presented with the Overrides Dialog select (Ctrl + CLick) the following 3 (SQLiteOpenHelper(context"Context,name:String,factory:CursorFactory,version:int) will be selected) :-
SQLiteOpenHelper(context"Context,name:String,factory:CursorFactory,version:int)
onCreate(.....
onUpgrade(.....
Then click OK.
You will then have :-
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
Define some constants
Between the class and the constructor add some constants so you have a single source for names of tables/columns etc e.g. :-
public static final String DBNAME = "tictactoe.db"; // Database name
public static final int DBVERSION = 1; // Database version #
public static final String TB_SCOREBOARD = "scoreboard"; // table name
public static final String COL_SCOREBOARD_ID = BaseColumns._ID; // use default id column name
public static final String COL_SCOREBOARD_USERNAME = "username";
public static final String COL_SCOREBOARD_SCORE = "score";
Ready to create the Table
The onCreate method will be called when you try to open the database (and the database is actually created). Generally it is here that you create the tables.
Note one of the more common issues newcomers have is that they think that onCreate runs every time a database is opened. It is not it only runs once when the database is first created.
as such any changes (say you add a new column) WILL NOT BE MADE if the databade still exists (easiest solution when developing an App is to delete the App's Data or uninstall the App and rerun the App).
So in the onCreate method :-
create a String of the SQL to create the table (i.e. the CREATE IF NOT EXISTS.... previously shown). However, do so utilising the CONSTANTS (see below).
call the SQLiteDatabase execSQL method to run the SQL.
Alter the constructor's signature (make it easier to call).
As the database name and version are known (they are constant) and that a cursor factory needn't be used (null will signify this) the super call in the constructor can be replaced with :-
super(context, DBNAME, null, DBVERSION);
Therefore the signature for the DBHelper class can be changed to :-
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
}
So the DBHelper class in full (at present) can be :-
public class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "tictactoe.db"; // Database name
public static final int DBVERSION = 1; // Database version #
public static final String TB_SCOREBOARD = "scoreboard"; // table name
public static final String COL_SCOREBOARD_ID = BaseColumns._ID; // use default id column name
public static final String COL_SCOREBOARD_USERNAME = "username";
public static final String COL_SCOREBOARD_SCORE = "score";
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String crtsql = "CREATE TABLE IF NOT EXISTS " +
TB_SCOREBOARD + // The table name
"(" +
COL_SCOREBOARD_ID + " INTEGER PRIMARY KEY," + //The _id column
COL_SCOREBOARD_USERNAME + " TEXT, " + // username column
COL_SCOREBOARD_SCORE + " INTEGER" + // score column (no trailing comma as last)
")" ;
sqLiteDatabase.execSQL(crtsql);
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
Stage 3 - TEST what has been so far
Believe it or not the above is sufficient to create the database and the table and hence the columns within the table (not to actually add any data or anything useful but at least).
Typically you would use the database in an activity. For the purposes of this testing a basic MainActivity will be used.
It's actually very simple we just create a DBHelper instance (passing the Context).
BUT doing so won't create a database it's only when either the getWritableDatabase or getReadableDatabase methods are called that an attempt is made to open or create the database. So a second line will do this (could be done in one line) :-
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DBHelper mDBHlpr = new DBHelper(this); // get DBHelper instance
mDBHlpr.getWritableDatabase(); // force an open (wouldn't normally do this)
}
}
Note you wouldn't normally have a call to getWritableDatabase as will be seen each method to access the table(s) will do this, so the very first time one of these would result in the onCreate method being called.
Note there is much confusion with getReadableDatabase, in short it doesn't make it so you can't update/change data unless the database can only be read.
i.e. getReadabledatabase will get a writable database unless in the very rare scenario the database can only be read (when getWritableDatabase would fail with an unable to open database error).
VERY RARELY is there any use coding getReadableDatabase.
Do the above and Run the App - nothing much will happen, but hopefully it shouldn't crash.
Now if you can (depends on emulator (I use genymotion which gives you root access)) use Android Studion's Device Explorer* to look at **/data/data/<your_package_name>/databases/ and hopefully you will see :-
package and actual database are highlighted.
database is just a file (can be copied and opened in other SQLite tools (can even be copied to android (emulator/device permitting))).
journal is SQLite's file that records what's being done and in cases of errors allows data to be rolled back (i.e. just accept it exists).
A believe that size should be 16K (depends upon data and structure of the database). It shouldn't be 0 though.
If you can't use Device Explorer then you can go into settings and check the App's data (if you have other uses of App data then check subtract this (check before running)), it should be 0 (after subtracting other data). in which case that's an indication that the database exists.
Stage 4 - Adding and Manipulating Data
At this stage a database exists with a table but no data itself exists. So a means of adding data (inserting rows) (a table has rows a row consisting of the columns as per the definition of the table).
It's no use adding data unless that data can be accessed so a means of extracting the data (querying) is required.
As a method of changing (updating) the score is required a means of doing this is required.
So what is needed now are 3 things :-
an insertRow method
a getAllData method (say to list scoreboard)
a updateScore method (which will adjust the score according to a number)
Typically such methods are added to the Database Helper (so they will be here)
The insertRow method
When inserting a row we need to add the name and the score (we could have defined the score column as score INTEGER DEFAULT 0 and then just the name would be required).
Although you don't know it yet id's can be very useful so the method will return the id of the newly inserted row, which due to using _id INTEGER PRIMARY KEY (and specifically this (or INTEGER PRIMARY KEY AUTOINCREMENT). will be generated automatically (i.e. the _id column is an alias of the very special but normally hidden rowid column (see link below for more info on rowid)).
the latter, AUTOINCREMENT, is very rarely needed but is seen very often more here SQLite Autoincrement, this also explains rowid)
So a method such as the following could be added :-
public long insertRow(String username, int initial_score) {
// SQL equivalent of :-
// INSERT INTO scoreboard (username,score) VALUES('the user name',0)
ContentValues cv = new ContentValues(); // Used by convenience method for column/value pairs
cv.put(COL_SCOREBOARD_SCORE,username); // The username to be added
cv.put(COL_SCOREBOARD_SCORE,initial_score); // The score to be added
SQLiteDatabase db = this.getWritableDatabase(); // Get a SQLiteDatabase instance
return db.insert(TB_SCOREBOARD,null,cv); // Insert it using conveniece method
/*
Note if row cannot be inserted then return will be -1
If inserted the id will be returned,
first ever insert will be 1,
then likely 2
then likely 3
NEVER ASSUME 1,2,3.......... though
ALWAYS ASSUME IT WILL BE A UNIQUE VALUE
i.e. NEVER CODE SPECIFIC ID's
*/
}
You may wish to read insert
The Activity could use this using for example :-
mDBHlpr.insertRow("Rumplestiltskin the 3rd",10000000); // The winner :)
mDBHlpr.insertRow("Fred Blogs",0); // New user would normally start with score 0
Adds 2 rows first with high score, 2nd as you would probably add a new user
The getAllData method
With Android you extract data into what's called a Cursor, which is like a spreadsheet it has rows and columns (columns as you specify so they needn't be all the columns, can also be other columns (e.g. derived/calculated or from other tables).
You create a Cursor (at least a normal one) by querying the table or tables in the database (note this doesn't cover all aspects). So use will be made of the convenience query method (well 1 of the 4) using :-
public Cursor getAlldata() {
// The columns to retrieve
String[] columns = new String[]{
COL_SCOREBOARD_ID,
COL_SCOREBOARD_USERNAME,
COL_SCOREBOARD_SCORE
};
// NOTE normally for all columns you would use the above but
// instead pass null as the 2nd parameter to the query method
return this.getWritableDatabase().query(
TB_SCOREBOARD,
columns,
null,
null,
null,
null,
null
);
}
You may wish to read query
This could be used in the Activity along the lines of :-
Cursor csr = mDBHlpr.getAlldata();
csr.close(); //YOU SHOULD ALWAYS CLOSE A CURSOR WHEN DONE WITH IT
The updateScore method
Without getting too complex and sticking to convenience methods the process of updating a score will :-
get the old score (according to id)
update the new score by adding the new score (if it's minus then reducing the score)
As such 2 parameters are required the id and the amount to adjust the score by.
-Id's should be long (you will see many uses of int but long copes with all possible id's).
-adjustment will be integer (long if very high scores are expected)
A diversion for getScoreById method
As getting a user's score may be useful another method will be created to do this. This also makes use of a Cursor that selects specific data rather than all via an SQL WHERE clause. So a method getScoreById will also be created. This will return the current score as an int and is passed a long as the id.
This could be :-
public int getScoreById(long id) {
int rv = -1; // just in case the id doesn't exist return -1 so invalid adjustment can be detected
String[] columns = new String[]{COL_SCOREBOARD_SCORE}; // only want the score
String whereclause = COL_SCOREBOARD_ID + "=?"; // will be WHERE _id=? (? replaced by respective whereargs element)
String[] whereargs = new String[]{String.valueOf(id)}; // ? will be replaced with id
Cursor csr = this.getWritableDatabase().query(
TB_SCOREBOARD,
columns,
whereclause,
whereargs,
null,
null,
null
);
if (csr.moveToFirst()) {
//rv = csr.getInt(0); // Hard coded column offsets bad so :-
rv = csr.getInt(csr.getColumnIndex(COL_SCOREBOARD_SCORE));
}
csr.close(); // Done with the cursor so close it
return rv; // return the current score
}
Back to the upDateScore method
Now that the score can be retrieved by the id via the getScoreById method then the the updateScore method could be :-
public boolean updateScore(long id, int adjustment) {
int newscore = getScoreById(id) + adjustment; // get the new score
// Check that the new score is valid (i.e. greater than 0)
// If it's invalid then don't do update by returning false but after
// issuing a message to the log (for development should be removed for production)
if (newscore < 0) {
Log.d("INVALIDSCORE",
"An invalid new score (less than 0) was returned. Update cancelled.");
return false;
}
// Prepare to use the update convenience method
String whereclause = COL_SCOREBOARD_ID + "=?";
String[] whereargs = new String[]{String.valueOf(id)};
ContentValues cv = new ContentValues();
cv.put(COL_SCOREBOARD_SCORE,newscore);
SQLiteDatabase db = this.getWritableDatabase();
// WARNING without a WHERE clause update would update ALL ROWS
// update returns number of rows updated as an int, so if this is
// greater than 0 true is returned else false.
return db.update(TB_SCOREBOARD,cv,whereclause,whereargs) > 0;
}
So the whole DBHelper class could be :-
public class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "tictactoe.db"; // Database name
public static final int DBVERSION = 1; // Database version #
public static final String TB_SCOREBOARD = "scoreboard"; // table name
public static final String COL_SCOREBOARD_ID = BaseColumns._ID; // use default id column name
public static final String COL_SCOREBOARD_USERNAME = "username";
public static final String COL_SCOREBOARD_SCORE = "score";
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String crtsql = "CREATE TABLE IF NOT EXISTS " +
TB_SCOREBOARD + // The table name
"(" +
COL_SCOREBOARD_ID + " INTEGER PRIMARY KEY," + //The _id column
COL_SCOREBOARD_USERNAME + " TEXT, " + // username column
COL_SCOREBOARD_SCORE + " INTEGER" + // score column (no trailing comma as last)
")" ;
sqLiteDatabase.execSQL(crtsql);
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
public long insertRow(String username, int initial_score) {
// SQL equivalent of :-
// INSERT INTO scoreboard (username,score) VALUES('the user name',0)
ContentValues cv = new ContentValues(); // Used by convenience method for column/value pairs
cv.put(COL_SCOREBOARD_SCORE,username); // The username to be added
cv.put(COL_SCOREBOARD_SCORE,initial_score); // The score to be added
SQLiteDatabase db = this.getWritableDatabase(); // Get a SQLiteDatabase instance
return db.insert(TB_SCOREBOARD,null,cv); // Insert it
/*
Note if row cannot be inserted then return will be -1
If insert the id will be returned,
first ever insert will be 1,
then likely 2
then likely 3
NEVER ASSUME 1,2,3.......... though
ALWAYS ASSUME IT WILL BE A UNIQUE VALUE
i.e. NEVER CODE SPECIFIC ID's
*/
}
public Cursor getAlldata() {
// The columns to retrieve
String[] columns = new String[]{
COL_SCOREBOARD_ID,
COL_SCOREBOARD_USERNAME,
COL_SCOREBOARD_SCORE
};
// NOTE normally for all columns you would use the above but
// instead pass null as the 2nd parameter to the query method
return this.getWritableDatabase().query(
TB_SCOREBOARD,
columns,
null,
null,
null,
null,
null
);
}
public boolean updateScore(long id, int adjustment) {
int newscore = getScoreById(id) + adjustment; // get the new score
// Check that the new score is valid (i.e. greater than 0)
// If it's invalid then don't do update by returning false but after
// issuing a message to the log (for development should be removed for production)
if (newscore < 0) {
Log.d("INVALIDSCORE",
"An invalid new score (less than 0) was returned. Update cancelled.");
return false;
}
// Prepare to use the update convenience method
String whereclause = COL_SCOREBOARD_ID + "=?";
String[] whereargs = new String[]{String.valueOf(id)};
ContentValues cv = new ContentValues();
cv.put(COL_SCOREBOARD_SCORE,newscore);
SQLiteDatabase db = this.getWritableDatabase();
// WARNING without a WHERE clause update would update ALL ROWS
// update returns number of rows updated as an int, so if this is
// greater than 0 true is returned else false.
return db.update(TB_SCOREBOARD,cv,whereclause,whereargs) > 0;
}
public int getScoreById(long id) {
int rv = -1; // just in case the id doesn't exist return -1 so invalid adjustment can be detected
String[] columns = new String[]{COL_SCOREBOARD_SCORE}; // only want the score
String whereclause = COL_SCOREBOARD_ID + "=?"; // will be WHERE _id=? (? replaced by respective whereargs element)
String[] whereargs = new String[]{String.valueOf(id)}; // ? will be replaced with id
Cursor csr = this.getWritableDatabase().query(
TB_SCOREBOARD,
columns,
whereclause,
whereargs,
null,
null,
null
);
if (csr.moveToFirst()) {
//rv = csr.getInt(0); // Hard coded column offsets bad so :-
rv = csr.getInt(csr.getColumnIndex(COL_SCOREBOARD_SCORE));
}
csr.close(); // Done with the cursor so close it
return rv; // return the current score
}
}
Stage 5 - Testing
The activity (based upon a new empty project) could now be :-
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DBHelper mDBHlpr = new DBHelper(this); // get DBHelper instance
//mDBHlpr.getWritableDatabase(); // force an open (wouldn't normally do this) Not needed now
mDBHlpr.insertRow("Rumplestiltskin the 3rd",10000000); // The winner :)
mDBHlpr.insertRow("Fred Blogs",0); // New user would normally start with score 0
mDBHlpr.updateScore(1,-9999999); //Set Rumplestiltskins's score to 0
// NOTE id should be 1 BUT hard coding id's is
// should be avoided (just used for demo purposes)
mDBHlpr.updateScore(2,1); // Increment Fred's score (see above re hard coded id's)
Cursor csr = mDBHlpr.getAlldata();
StringBuilder sb = new StringBuilder();
// Do something with the Extracted Data
while (csr.moveToNext()) { // Loop through all rows
long userid = csr.getLong(csr.getColumnIndex(DBHelper.COL_SCOREBOARD_ID));
String username = csr.getString(csr.getColumnIndex(DBHelper.COL_SCOREBOARD_USERNAME));
int userscore = csr.getInt(csr.getColumnIndex(DBHelper.COL_SCOREBOARD_SCORE));
sb.append("\n\tUsername=");
sb.append(username);
sb.append((" (ID="));
sb.append(userid);
sb.append(") Score=");
sb.append(userscore);
sb.append(".");
}
csr.close();
Log.d("SCOREBOARD",sb.toString());
}
}
Note cursor handling added
Result
note after numerous runs which will add duplicate usernames but with different id's)
:-
05-18 12:09:46.750 3018-3018/? D/INVALIDSCORE: An invalid new score (less than 0) was returned. Update cancelled.
05-18 12:09:46.754 3018-3018/? D/SCOREBOARD: Username=null (ID=1) Score=1.
Username=null (ID=2) Score=5.
Username=null (ID=3) Score=10000000.
Username=null (ID=4) Score=0.
Username=null (ID=5) Score=10000000.
Username=null (ID=6) Score=0.
Username=null (ID=7) Score=10000000.
Username=null (ID=8) Score=0.
Username=null (ID=9) Score=10000000.
Username=null (ID=10) Score=0.
Invalid Score is because once ID 1 is down to 1 the adjustment of -99999999 will be less than 0.
ID 2's score is 5 due to 5 runs (i.e. 10 rows/users).
Note
The above is a fully working albeit it not that useful, introduction/answer. As such any subsequent questions should really be other questions on Stack Overflow.*

sql always return the same data

In my project, I use SQL (SQLite since its android) to save my data.
I encountered some odd problem:
In my application, I have three tabs and in order to know which data belong to which tab I have in my chart a column for each tab. the number in the column (Integer) represent if and how many of that data suppose to be in this tab.
So, when the tab initialized, it reads from the chart, and using the relevant column, it can tell which data needs to be read and how many.
When I retrieve data from the server (the database of the server is not related to the problem at hand), I check if the data is new or that I already have a similar one in my SQL DB. If its new, I add it to the SQL chart and put 1 in the relevant column. If it already exists, it checks if the data in the SQL is updated (it update the data if necessary) and add 1 to the relevant column (the same column it puts 1 in it in case the data is not in the SQL DB).
now here's my problem:
when it reads from the SQL DB to see the number in the column, so it can add 1 to it and then update the chart, it always return 1 regardless the actual number that in the column in that moment (I know its not really 1 because when I read from the DB in the same column from other places in my app it does read the actual number).
Since in other places in my app it doesn't happen I tried to see what is the difference between the places but I did not find anything wrong (the only difference is that I used WritableDatabase instead of just ReadableDatabase in that time but that should not be an issue as far as I know, at least not in such case).
the code where the problem occurs (the problem is with COL_CART):
writable = db.getWritableDatabase();
Cursor cursor = writable.query(
ShopContract.ShopChart.TABLE_NAME,
new String[]{ShopContract.ShopChart.COL_NAME, ShopContract.ShopChart.COL_PRICE, ShopContract.ShopChart.COL_PIC, ShopContract.ShopChart.COL_CART},
ShopContract.ShopChart.COL_PROD_ID + " = '" + product.getProd_id() +"'",
null,
null,
null,
null
);
if(cursor.moveToFirst()){
//check if the data match, if not, replace.
if(!cursor.getString(cursor.getColumnIndex(ShopContract.ShopChart.COL_NAME)).equals(product.getName())){
ContentValues values = new ContentValues();
values.put(ShopContract.ShopChart.COL_NAME,product.getName());
update(values);
}
if(cursor.getDouble(cursor.getColumnIndex(ShopContract.ShopChart.COL_PRICE)) != product.getPrice()){
ContentValues values = new ContentValues();
values.put(ShopContract.ShopChart.COL_PRICE,product.getPrice());
update(values);
}
if(!cursor.getString(cursor.getColumnIndex(ShopContract.ShopChart.COL_PIC)).equals(product.getPicture())){
ContentValues values = new ContentValues();
values.put(ShopContract.ShopChart.COL_PIC,product.getPicture());
update(values);
}
//check how many there are already in cart (already in the cursor) and update it to be ++
Log.d(TAG, "run: the number in col_cart is: " + cursor.getInt(cursor.getColumnIndex(ShopContract.ShopChart.COL_CART)));
int inCart = cursor.getInt(cursor.getColumnIndex(ShopContract.ShopChart.COL_CART)) + 1; // because of the new product we just added
Log.d(TAG, "run: inCart = " + inCart);
ContentValues values = new ContentValues();
values.put(ShopContract.ShopChart.COL_CART,inCart);
Log.d(TAG, "run: change cart");
update(values);
edit:
here is the update method code (the writable is being initialized in the code above):
private SQLiteDatabase writable;
private void update(ContentValues values){
writable.update(ShopContract.ShopChart.TABLE_NAME, values, ShopContract.ShopChart.COL_PROD_ID + " = '" + product.getProd_id() +"'",null);
}

SQLite get row data using SELECT

Using SQLite Database, I have a table with 6 columns in each row as the rows are added. The first column is the name of the "person." I have it so when you click on the person in a listview, it brings up a screen with 5 edit texts. You fill them out and submit it and it adds it to another row in the database.
To retrieve that data later on, I am trying to use SELECT by the name to get it, but cannot figure out how this works.
public Cursor getChildRulesInformation(DatabaseOperations dop, String name) {
dop.getReadableDatabase().execSQL("SELECT * FROM "+CHILD_RULES_TABLE_NAME+" WHERE "+CHILD_NAME + "=\""+ name+"\"");
}
What do I do with that to retreive every column inside of that specific row. I am confused on the process to get it out.
Any lead in the write direction would be greatly appreciated. Thanks. If you need any more information please let me know.
Try this,
public Cursor getChildRulesInformation(DatabaseOperations dop, String name) {
return dop.getReadableDatabase().rawQuery("SELECT * FROM "+CHILD_RULES_TABLE_NAME+" WHERE "+CHILD_NAME+" = '"+ name+"' ", null);}
Do not use execSql for getting data, that is only for sending data to the database. Instead, use rawQuery if you want to use the String. So it would be:
public Cursor getChildRulesInformation(DatabaseOperations dop, String name) {
return dop.getReadableDatabase().rawQuery("SELECT * FROM "+CHILD_RULES_TABLE_NAME+" WHERE "+CHILD_NAME + "='"+ name+"'", null);
}
This will return you the cursor object that you are looking for.

How to get details of the inserted row from sqlite, android

I have created a table (Detail) with two columns one column (ID) is primary and auto-increment while the other column (EmpName) is varchar type. While inserting data in database i am only inserting data in EmpName column using the following code. But I want to know the details of that record (ID and name but i know only name and ID is created by database). I can write another select statement and get the last record of the cursor after executing the insert query but if another insertion will be done before executing the select statements then my database will give me wrong result. How can i get the details?
ContentValues initialvalues = new ContentValues();
initialvalues.put("EmpName", "John");
db.insert("Detail", null, initialvalues);// db is database
The above code is working fine for insertion of data in database but How to get that details?
please the API:SQLiteStatement.executeInsert()
According to the source code:
/**
* Execute this SQL statement and return the ID of the row inserted due to this call.
* The SQL statement should be an INSERT for this to be a useful call.
*
* #return the row ID of the last row inserted, if this insert is successful. -1 otherwise.
*
* #throws android.database.SQLException If the SQL string is invalid for
* some reason
*/
public long executeInsert() {
BlockGuard.getThreadPolicy().onWriteToDisk();
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
long timeStart = SystemClock.uptimeMillis();
mDatabase.lock();
acquireReference();
try {
native_execute();
mDatabase.logTimeStat(mSql, timeStart);
return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1;
} finally {
releaseReference();
mDatabase.unlock();
}
}

Categories

Resources