I need to process a SQLite dataset in an Android system.
In my dataBaseHelper file (DataBaseAccessor) I have the following code (which when attached to a listview shows the relevant data).
public static ArrayList<QuestionListQuestion> getQuestionListQuestions(long id){
String qry = "select QuestionListQuestionID, QuestionListQuestionQuestionListID, QuestionListQuestionQuestionID, QuestionListQuestionSortOrder, QuestionListQuestionSupplementalQuestionIDYes, QuestionListQuestionSupplementalQuestionIDNo, QuestionListQuestionSupplementalQuestionIDText, QuestionListQuestionSurveyGroupID from QuestionListQuestion where QuestionListQuestionQuestionListID=" + id;
ArrayList<QuestionListQuestion> list = new ArrayList<QuestionListQuestion>();
try{
Cursor cursor = wdb.rawQuery(qry, null);
while (cursor.moveToNext()) {
QuestionListQuestion questionlistquestion = new QuestionListQuestion();
questionlistquestion.QuestionListQuestionID = cursor.getLong(0);
questionlistquestion.QuestionListQuestionQuestionListID = cursor.getLong(1);
questionlistquestion.QuestionListQuestionQuestionID = cursor.getLong(2);
questionlistquestion.QuestionListQuestionSortOrder = cursor.getLong(3);
questionlistquestion.QuestionListQuestionSupplementalQuestionIDYes = cursor.getString(4); questionlistquestion.QuestionListQuestionSupplementalQuestionIDNo = cursor.getString(5);
questionlistquestion.QuestionListQuestionSupplementalQuestionIDText = cursor.getString(6);
questionlistquestion.QuestionListQuestionSurveyGroupID = cursor.getLong(7);
list.add(questionlistquestion);
}
cursor.close();
}
catch (Exception e) {
e.printStackTrace();
}
return list;
}
I now need to extend the system so that I can process the data and create new records in another table based on the original list returned.
I tried the following attached to a button (selecting the relevant list ID from a spinner):-
QuestionListID = (String) SiteGenerateQuestions.this.spnQuestL.getSelectedItem().toString();
long SpinnerSelectedBT;
SpinnerSelectedBT = GenerateQuestions.this.spnQuestL.getSelectedItemId();
list = DatabaseAccessor.getQuestionListQuestions(SpinnerSelectedBT);
for (int i=0; i < list.size(); i++){
Toast.makeText(SiteGenerateQuestions.this," list.get(" + i + ") = " + list.get(i) + " " , Toast.LENGTH_SHORT).show();
}
The Toast displays the following:-
list.get(0) = com.tw.question.entity.QuestionListQuestion#407a6F70
list.get(1) = com.tw.question.entity.QuestionListQuestion#407bc170
etc...
How can I get access to the actual data instead of ... .entity.QuestionListQuestion#407bc170 or am I completely off-track?
Many Thanks
I agree with #wsanville the get() method will return the object in that location of the list. When you print out an object (in a toast, log, System.out.println etc) it will use the toString() value in the printout. The default toString() is the package name followed by # which is followed by a hex representation of that object. Your class will need to override the toString() method so when you use get() it will print out whatever you put in your toString() method.
The output you're seeing is because you haven't implemented the toString() method of your QuestionListQuestion class. Other than that, it seems like you do have the data you're looking for. Just try outputting the ID of your object, rather than concatenating the object itself (which will call toString() under the hood).
Also, since it looks like you're doing a database operation when a button gets clicked, make sure to do your database operations outside the UI thread. Check out the docs for a high level overview. You might want to use an AsyncTask for your database operation in question.
Related
I am trying to learn retrofit and I have made successful attempts at posting data and now I am trying to retrieve JSON array which looks as follows:
{
"result": "success",
"message": "All Questions Have Been Selected",
"question": {
"all_question_ids": ["1","2","3"]
}
}
I am using the following getter
public ArrayList getAll_question_ids(){
return all_question_ids;
}
I am retrieving using Retrofit as follows
if (resp.getResult().equals(Constants.SUCCESS)) {
SharedPreferences.Editor editor = pref.edit();
Log.d("Question_IDs", "getAllQuestionID() = " + response.body().getQuestion().getAll_question_ids() );
editor.putString(Constants.All_QUESTION_IDS,((resp.getQuestion().getAll_question_ids().toString())));
editor.apply();
}
progress.setVisibility(View.INVISIBLE);
It is here that I am stuck, as I am retrieving the array ok but I am unsure how to loop out the Array which is now stored in Shared Preferences.
When I place a toast to show me how the IDs are coming across, my toast confirms the data as [1,2,3]
The goal is to add a dynamic button and the individual ID, i.e button 1, button 2 etc every-time the loop is iterated.
I have tried the following:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
for (int i =0; i < questionNumber.length(); i++) {
try {
/*Dynamically create new Button which includes the question name
*/
AppCompatButton btn_question = new AppCompatButton(getActivity());
/*LayoutParams (int width, int height,float weight)
As LayoutParams is defaulted in px, I have called a method called dpToPX to make sure
the dynamically added EditText is the same size on all devices.
*/
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpToPx(280), dpToPx(45), 1);
btn_question.setBackgroundColor(Color.parseColor("#3B5998"));
btn_question.setTextColor(Color.WHITE);
// btn_question.setText(String.valueOf(x));
btn_question.setText("Question "+ pref.getString(Constants.All_QUESTION_IDS,""));
btn_question.setGravity(Gravity.CENTER);
//generate unique ID for each new EditText dynamically created
View.generateViewId();
//Log.d("TEST VALUE", "Question1 generated ID = " + btn_question.generateViewId());
params.setMargins(0, dpToPx(10), 0, dpToPx(10));
btn_question.setPadding(0, 0, 0, 0);
btn_question.setLayoutParams(params);
allEds.add(btn_question);
mLayout.addView(btn_question);
} catch (Exception e) {
Log.d(TAG, "Failed to create new edit text");
}
}
However the above is adding the value as it appears in the array e.g [1,2,3] which is obviously not what I want.
I have added a photo in case my explanation isn't clear. I want a button with 1 number added to it each time the loop iterates but I am unable to figure this out.
I have looked through lots of resource but cannot find an answer that is relevant to my problem, although, if there is, I am not familiar enough to recognise a similar issue.
If someone can offer some assistance, I would appreciate it!
When you call editor.putString(Constants.All_QUESTION_IDS,((SOMETHING.toString())));, what is actually stored depends on the implementation of the toString method in the type of SOMETHING (in this case String[]). So avoid doing that. Instead, since you're already using Gson or Jackson (or others), store the question_idsas JSON:
final String jsonIds = gson.toJson (resp.getQuestion().getAll_question_ids());
editor.putString(Constants.All_QUESTION_IDS, jsonIds);
Your actual stored value no longer depends on the implementation of something that you don't control (String[].toString). It is a valid JSON array and regardless of what tool/library you use to read it back, it's valid.
Now, to read back the stored data:
final String storedJson = pref.getString(Constants.All_QUESTION_IDS, null);
if (null == storedJson) {
// TODO: No question ids found
}
final String[] ids = gson.fromJson (storedJson, String[].class);
for (int i = 0; i < ids.length; i++) {
// make your buttons
}
This is a problem of saving and then reading out a List of items (in this case, String instances).
You've chosen to save the list by calling editor.putString() with a value of getAll_question_ids().toString(). That toString() call is going to return a string representation of your list, or, in other words, a String instance with the value [1, 2, 3]. At this point, you no longer have a List proper, but a String that looks like a list.
This is all technically fine, but it means you have to take this into account when you're trying to read out that list.
You've written this to read the list back out:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
Once this line executes, questionNumber will be a String instance with the value [1, 2, 3]. Again, this is fine, but now we come to the key point: we have to convert this String back into a List.
If you know for sure that the values in this list won't have commas in them, you can do it easily:
Trim the braces off the string using substring()
Create a String[] using split()
Convert your array to a list using Arrays.asList() (you could even skip this step since iterating over an array is just as easy as iterating over a list)
Put that together and you get:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
questionNumber = questionNumber.substring(1, questionNumber.length() - 1);
String[] array = questionNumber.split(", ");
List list = Arrays.asList(array);
At this point, you can iterate over your array or list:
for (String value : list) {
...
btn_question.setText("Question " + value);
...
}
I have a problem with Lists in Java and Android. I read and save all user contacts in a List with this code:
List<ContactInfo> list = new ArrayList<ContactInfo>();
ContactInfo contactInfo = new ContactInfo();
do {
contactInfo.name = people.getString(indexName);
contactInfo.phone = people.getString(indexNumber);
list.add(contactInfo);
} while (people.moveToNext());
In the do-while everything is OK, name and phone is correct but the list don't save correctly values (To be specific, it saves correctly the values but out of the do-while every value is replaced with the last inserted value).
It just saves the last contact in every position but if I try to debug what he add in every position during the do-while it says the correct values.
It's the code I used to check every item out of the do-while
int i = 0;
for(ContactInfo info : list)
{
Log.d(newMessage.TAG, "contactInfo(" + i + "): name = " + info.name + " ; phone = " + info.phone);
++i;
}
I have fixed the problem with this edit:
do {
contactInfo = new ContactInfo();
contactInfo.name = people.getString(indexName);
contactInfo.phone = people.getString(indexNumber);
list.add(contactInfo);
} while (people.moveToNext());
It creates a different instance of ContactInfo every contact but why don't work if I create one out of the do-while?
And, if I create a different instance for every contact it can cause memory-leak / out-of-memory or something similar if number of contacts are too much? Or maybe just make the application slow, because the GC should clear the memory?
The Problem is that the object will be the same when you do it your way. The objectreference of the object is the same all the time, so the only object which is added it the first one.
Greets
I have an issue that may in fact be a design issue but I am struggling to find a way around it.
In my sqlite database in my android application, I have a table called Customer. This has around 40 columns of various string and int data types. In practice, any of these columns may or may not be null.
In my code, I have a function simply called getCustomer(), which queries the database for a specific customer, and places all their cells from the database into a Customer class, which contains variables for each column. I can then pass that customer object around as I wish. The getCustomer() function returns this Customer object.
My problem is integers which may be null. I am familiar with how int cannot be null in java, but how Integer can be null. But my problem actually lies in the fact that the cells in the database can be empty (eg null).
For string columns, I simply do this:
Customer cust = new Customer();
cust.firstName = cursor.getString(0);
If cursor.getString(0); returns a null value, then the firstName variable is assigned null. Nice and simple. However with an int:
Customer cust = new Customer();
cust.daysSinceJoining = cursor.getInt(5);
The above crashes at run-time if daysSinceJoining is null. So I tried the following:
Customer cust = new Customer();
if (cursor.getInt(5) != null)
cust.daysSinceJoining = cursor.getInt(5);
However this gives me a compiler error, as you cannot use an int in a null comparison like that.
How can I get around this problem? How can I retrieve an int from an sqlite database when the int value could be null?
#sanders is right about the isNull() method and here's how you edit your code to use it:
Customer cust = new Customer();
if (!cursor.isNull(5))
cust.daysSinceJoining = cursor.getInt(5);
You could try the isNull() function.
Please take a look to that answer:
getInt null-constraints
I think that should work for you:
int lIndex = cursor.getColumnIndexOrThrow(COLUMN);
Integer lInteger = null;
if (!cursor.isNull(lIndex)
lInteger = cursor.getInt(lIndex);
You can create a cursor wrapper and add new functionality to the cursor class:
public class CustomCursor extends CursorWrapper {
public CustomCursor(Cursor cursor) { super(cursor); } //simple constructor
public Integer getInteger(int columnIndex) { // new method to return Integer instead of int
if (super.isNull(columnIndex)){
return null;
}else{
return super.getInt(columnIndex);
}
}
}
Example usage:
Cursor defaultCursor = db.rawQuery("select null,2 ", null);
defaultCursor.moveToFirst();
CustomCursor customCursor = new CustomCursor(defaultCursor);
customCursor.moveToFirst(); //custom cursor can do anything that default cursor can do
int defaultInt0 = defaultCursor.getInt(0); //nulls are usually converted into zero
int defaultInt1 = defaultCursor.getInt(1); //2 is correct
int customInt0 = customCursor.getInt(0); //these 2 are same as above , ie zero and 2
int customInt1 = customCursor.getInt(1);
Integer customInteger0 = customCursor.getInteger(0); // this will give a null Integer
Integer customInteger1 = customCursor.getInteger(1); // this will give a 2 Integer
Log.v("custom log.v call ", "lets see what outputs, null is usually converted to the word 'null' by the String.valueOf method :"
+String.valueOf(defaultInt0)+","+String.valueOf(defaultInt1)+","
+String.valueOf(customInt0)+","+String.valueOf(customInt1)+","
+String.valueOf(customInteger0)+","+String.valueOf(customInteger1)
);
//V: lets see what outputs, null is usually converted to the word 'null' by the String.valueOf method :0,2,0,2,null,2
if (cursor.getInt(5) !=0){
cust.daysSinceJoining = cursor.getInt(5);
}
or
int index = cursor.getColumnIndex(KEY_NAME);
Integer x = null;
if (!cursor.isNull(index)
cust.daysSinceJoining = cursor.getInt(5);
}
see the documentation
getInt() documentation:
I want to save multiple files to my database one by one.
And what happen here using my codes is this:
What I want to happen is like this one:
here is my code:
//Arraylist for getting the multiple brand code
ArrayList<String> content = new ArrayList<String>();
for (int j=0; j<checkSelected.length; j++) {
if(checkSelected[j]==true) {
String values = BrandListAdapter.mListItems.get(j);
//content.add(values);
Cursor rSubBrand = databaseHandler.getReport_SubBrandCode(values);
String SubBrandCode = rSubBrand.getString(rSubBrand.getColumnIndex(Constants.SUBBRAND_CODE));
content.add(SubBrandCode);
//Casting and conversion for SubBrand Code
String subBrand = content.toString();
//SAVE SUBBRAND
databaseHandler.SaveSubBrand(new Cons_iReport (ReportCode, subBrand));
Toast.makeText(getApplicationContext(), subBrand, Toast.LENGTH_LONG).show();
}
}
Mistakes:
content.add(SubBrandCode);
do you know how to remove the '[ ]' in the saved data? (e.g. [AC001]) All I want to save is the AC001 only.
Solutions:
Call clear() method of ArrayList before adding new values into it.
Its giving you [AC001] as you are subBrand by doing content.toString();. Don't convert it to string, instead use content.getString(position) and it will give you String value.
I am testing the capabilities of the device -- to show the customer the size of data that can be stored inside the device, how fast it can be retrieved, how fast the search works, etc.
I am using my content provider to access the product database table with few columns. I have already moved the code to the content provider to avoid the extra communication when inserting the test records. The following code is called via menu from an activity to fill the table with the test content
Uri uri = Uri.parse(DemoContentProvider.PRODUCTS_CONTENT_URI + "/insertdemo");
getContentResolver().insert(uri, null);
The URI is recognized in the .insert method of the content provider and the following private method (of the same content provider) is called to fill the table (notice the 100 thousands of items):
private void insertDemoProducts() {
for (int i = 1; i <= 100000; ++i) {
String id = Integer.toString(i);
insertProduct(id, "Test product " + id, "100", "75.50", "70.27");
}
}
The inner insertProduct() looks like that:
private void insertProduct(String code, String name, String stock,
String price, String listprice) {
SQLiteDatabase sqlDB = database.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ProductTable.COLUMN_CODE, code);
values.put(ProductTable.COLUMN_NAME, name);
values.put(ProductTable.COLUMN_STOCK, stock);
values.put(ProductTable.COLUMN_PRICE, price);
values.put(ProductTable.COLUMN_LISTPRICE, listprice);
sqlDB.insert(ProductTable.TABLE_PRODUCT, null, values);
}
It works, but it takes "forever". How can I make it faster? What is the fastest method you know to fill the table?
Just some numbers to consider: 1000 items takes about 20 seconds to be created.
You need to use transactions when writing to a sqlite-database, otherwise it will persist the data for every insert i.e save it to sd which will take "forever".
for instance, make insertProduct take a list of products and save them in one transaction:
private void insertProducts(List<Product> products) {
try {
db.beginTransaction();
for(Product product : products) {
insertProduct(...);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
This is how you can implement it in your existing code:
private void insertDemoProducts() {
SQLiteDatabase sqlDB = database.getWritableDatabase();
try {
sqlDB.beginTransaction();
for (int i = 1; i <= 100000; ++i) {
String id = Integer.toString(i);
insertProduct(id, "Test product " + id, "100", "75.50", "70.27");
}
sqlDB.setTransactionSuccessful();
} finally {
sqlDB.endTransaction();
}
}
Anyway, I am not completely satisfied with the accepted question because I do not understand the reason why adding the transaction makes it faster.
Looking at the Android sources, I have found that the sqlDB.insert(...) calls insertWithOnConflict(...) and that one construct the string for the SQL command using the StringBuilder class (with questionmarks as placeholders for the inserted values). Only then the string is passed to the SQLiteStatement constructor together with array of the inserted values. This means that string with the SQL command is being built again and again.
Further, a string representation of an SQL command template can be precompiled thus avoiding also the repeated compilation of the command. Then .bindXxx and .execute methods can be used for inserting the wanted records into the table. When put together, I did use the followig code (iside the outer transaction as Dean suggested):
StringBuilder sql = new StringBuilder();
sql.append("INSERT INTO ");
sql.append(ProductTable.TABLENAME);
sql.append("(");
sql.append(ProductTable.COLUMN_CODE);
sql.append(",");
sql.append(ProductTable.COLUMN_NAME);
sql.append(",");
sql.append(ProductTable.COLUMN_STOCK);
sql.append(",");
sql.append(ProductTable.COLUMN_PRICE);
sql.append(",");
sql.append(ProductTable.COLUMN_LISTPRICE);
sql.append(") VALUES (?, ?, 100, 75.50, 70.27)");
SQLiteStatement insert = sqldb.compileStatement(sql.toString());
for (int i = 1; i <= 300000; ++i) {
insert.bindLong(1, i);
insert.bindString(2, "Test product " + i);
insert.execute();
}
When compared with adding the transaction only, the result is about 3-times faster. The 300 thousands records were inserted in about 3 minutes and 15 seconds on Nexus 7.