Good Day,
I've been working on an update in an android project, and came across an issue. I have to read questions from an SQLite database which i've done successfully by loading it into a multi-dimensional array as shown below in my database helper class:
public String getSome(int s,int t, String Table_Name){
String selectQuery = "SELECT * FROM " + Table_Name;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
int rows = cursor.getCount();
int num=0;
int col = 0;
String[][] base = new String[rows][13];
if (cursor.moveToFirst()) {
do {
for (col=0;col<13;++col ){
base[num][col] = (cursor.getString(col));}
++num;
} while (cursor.moveToNext());
return base[s][t];
}
return null;
}
With that done, i read the questions as such in my question class:
public void database_calls(){
setCourseTag(courseTag);
myDbHelper = new DataBaseHelper(getActivity());
try {
myDbHelper.createDataBase();
} catch (IOException ioe) {
throw new Error("Unable to create database");
}
try {
myDbHelper.openDataBase();
}catch(SQLException sqle){
throw sqle;
}
String no= myDbHelper.getSome(ques,0, getCourseTag());
String qu = myDbHelper.getSome(ques,1, getCourseTag());
String a = myDbHelper.getSome(ques,2, getCourseTag());
String b = myDbHelper.getSome(ques,3, getCourseTag());
String c = myDbHelper.getSome(ques,4, getCourseTag());
String d = myDbHelper.getSome(ques,5, getCourseTag());
ans = myDbHelper.getSome(ques,6, getCourseTag());
img = Integer.parseInt(myDbHelper.getSome(ques,8, getCourseTag()));
exp = myDbHelper.getSome(ques,9, getCourseTag());
year = myDbHelper.getSome(ques,10, getCourseTag());
questionImage = myDbHelper.getSome(ques,11, getCourseTag());
length = myDbHelper.getMax(getCourseTag());
}
So recently, i tried to use the year column, (i.e, column 10) to qualify the questions chosen for each quiz session, so that the user may be able to select the questions from any year, he/she wants to attempt. In order to do this, i used a loop at the beginning of the activity to filter out only the required year past questions. Then i transferred the indices of each question to a set, bal , from where it is iterated and so on..
public void countYearQuestions(){
for(int y = 0; y < length; ++y){
//year = myDbHelper.getSome(y,10, getCourseTag());
if (selectedYear.equals(myDbHelper.getSome(y,10, getCourseTag())))
bal.add(y);
}
}
Here, length is the size of the entire question database, for the course, (indicated by getCourseTag()). The code works quite alright. But it takes a whole 8-9secs!! for the activity to load. Any help on how to reduce this loading time would be appreciated.
The way you're doing this is pretty efficient in slowing everything down (and your helper helps you with it a lot):
String no= myDbHelper.getSome(ques,0, getCourseTag());
In each such line you execute a query, create a 2D array holding the whole table and throw nearly everything away. And you fail to close the Cursor.
So you need 12 values from a table and read instead the whole table 12 times.
Related
I am working with JPA, my web application is taking 60 sec to execute this method, I want to execute it faster how to achive ?
public boolean evaluateStudentTestPaper (long testPostID, long studentID, long howManyTimeWroteExam) {
Gson uday = new Gson();
Logger custLogger = Logger.getLogger("StudentDao.java");
// custLogger.info("evaluateTestPaper test paper for testPostID: " +
// testPostID);
long subjectID = 0;
// checking in table
EntityManagerFactory EMF = EntityManagerFactoryProvider.get();
EntityManager em = EMF.createEntityManager();
List<StudentExamResponse> studentExamResponses = null;
try {
studentExamResponses = em
.createQuery(
"SELECT o FROM StudentExamResponse o where o.studentId=:studentId And o.testPostID=:testPostID and o.howManyTimeWroteExam=:howManyTimeWroteExam")
.setParameter("studentId", studentID).setParameter("testPostID", testPostID)
.setParameter("howManyTimeWroteExam", howManyTimeWroteExam).getResultList();
System.out.println("studentExamResponses--------------------------------------------------"
+ uday.toJson(studentExamResponses) + "---------------------------------------");
} catch (Exception e) {
custLogger.info("exception at getting student details:" + e.toString());
studentExamResponses = null;
}
int studentExamResponseSize = studentExamResponses.size();
if (AppConstants.SHOWLOGS.equalsIgnoreCase("true")) {
custLogger.info("student questions list:" + studentExamResponseSize);
}
// Get all questions based on student id and test post id
List<ExamPaperRequest> examPaperRequestList = new ArrayList<ExamPaperRequest>();
List<Questions> questionsList = new ArrayList<Questions>();
// StudentExamResponse [] studentExamResponsesArgs =
// (StudentExamResponse[]) studentExamResponses.toArray();
// custLogger.info("Total questions to be evaluated: " +
// examPaperRequestList.size());
List<StudentTestResults> studentTestResultsList = new ArrayList<StudentTestResults>();
StudentTestResults studentTestResults = null;
StudentResults studentResults = null;
String subjectnames = "", subjectMarks = "";
int count = 0;
boolean lastIndex = false;
if (studentExamResponses != null && studentExamResponseSize > 0) {
// studentExamResponses.forEach(studentExamResponses->{
for (StudentExamResponse o : studentExamResponses.stream().parallel()) {
// 900 lines of coade inside which includes getting data from database Queries
}
}
As #Nikos Paraskevopoulos mentioned, it should probably be the ~900 * N database iterations inside that for loop.
I'd say to avoid DB iterations as much as you can, specially inside a loop like that.
You can try to elaborate your current StudentExamResponse sql to englobe more clauses - those you're using inside your for mainly, which could even diminish the amount of items you iterate upon.
My guess would be your select query is taking time.
If possible, set query timeout to less than 60 seconds & confirm this.
Ways of setting query timeout can be found out there - How to set the timeout period on a JPA EntityManager query
If this is because of query, then you may need to work to make select query optimal.
I'm trying to make a java quiz linked to an sqlite database from where it's getting the informations (Questions and answers).
Every quiz has n questions.
A question has 4 options.
A question can have more than one true question.
so My database tables are:
Quiz(id_quiz, quiz_name)
Question (id_question, question, #id_quiz)
Answer (id_answer, statut, answer, #id_question)
So I created a first frame to select the quiz that we want to pass, it's working with a function that's calling the new Frame depending on the selected quiz.
My problem is when I compare the selected answers with the true answer that must be selected.
this is my code:
public boolean compare(List<Integer> trueAnswer, List<Integer> selected){
if (trueAnswer.equals(selected)){
return true;
} else {
return false;
}
}
The first step is to get the question in the jLabel, and the answers of that question too:
public List<Answer> showQuestion(int index){
in = fillQuestion(idQ).get(index).getIdQuestion();
jQuestion.setText(fillQuestion(idQ).get(index).getQuestion());
try {
con = DriverManager.getConnection("jdbc:sqlite:myflightdb.db", "", "");
String sql2 = ("select r.id_answer ,r.answer, r.statut from question q, answer r where q.id_question = ? and r.id_question = q.id_question ;");
PreparedStatement psmt2;
psmt2 = con.prepareStatement(sql2);
psmt2.setInt(1, in);
List<Answer> listRep = new ArrayList<Answer>();
rs2 = psmt2.executeQuery();
while(rs2.next()){
int idR = rs2.getInt("id_answer");
String rep = rs2.getString("answer");
String statut = rs2.getString("statut");
Reponse r = new Reponse(idR, rep, statut);
listRep.add(r);
}
return listRep;
} catch (SQLException ex) {
System.out.println(ex.getMessage());
}
return null;
}
the function getTrueAnswer is made in order to get the list of true answers for question:
public List<Integer> getTrueAnswer(int index){
int idquestion;
idquestion = fillQuestion(idQ).get(index).getIdQuestion();
List<Integer> rep = new ArrayList<Integer>();
try {
con = DriverManager.getConnection("jdbc:sqlite:/Users/gate11/Desktop/MyFlight/myflightdb.db", "", "");
String sql = "select r.id_reponse from Reponse r where r.id_question = ? and r.statut like 'true';";
PreparedStatement psmt = con.prepareStatement(sql);
psmt = con.prepareStatement(sql);
psmt.setInt(1, idquestion);
rs = psmt.executeQuery();
while (rs.next()){
int idTrueAnswer= rs.getInt("id_reponse");
rep.add(idTrueAnswer);
}
//JOptionPane.showMessageDialog(null, Arrays.toString(rep.toArray()), "Selected IDs", JOptionPane.INFORMATION_MESSAGE);
return rep;
} catch (Exception e){
System.out.println(""+e.getMessage());
}
return null;
}
The button next must compare the selected jCheckBoxes with the true questions, if it's the same, the mark must be positif (so I can add the point to the result) or add 0 if the user doesn't choose the true one.
The problem is for the first time, it's working (I made the JOptionPanes to see the lists) so if I selected the first question, I have the selected is [1] and the true answer is [2]
but in the second question, if I select 1 I have the list is [1,1] for the third one [1,1,1] the fourth [1,1,1,1] etc ...
And I don't have any loop I can't find the problem.
If there is any idea please don't hesitate.
P.S: I did the 4 checkboxes because when I tried to add it in a for loop like that:
for (Answer a:listRep){
JcheckBox mycheck = new JcheckBox();
mycheck.setText(a.getQuestion());
mypanel.add(mycheck);
}
I found the problem, the itemListener must be added to the jCheckBoxes in the initialisation of the components.
I am developing a simple social media for my case study. I was able to retrieve the post from the people the user follows. Here's the screenshot:
As you can see, the problem is that the posts were not sorted according to date/id. Instead, it is sorted according to the people the user follows. It is because I am only merging the cursors using mergecursor. Here's a part of my code:
ListView listviewFeed = (ListView) findViewById(R.id.listviewFeed);
Cursor cursorFeed = DataAdapters.getFeed(dbHelper, strUserID);
//This code is for retrieving user's own posts
Cursor cursorFollowing = DataAdapters.getFollowing(dbHelper,strUserID);
//This code is for retrieving the followed users.
if(cursorFollowing.getCount()>0) {
for (int intCtr = 0; intCtr < cursorFollowing.getCount(); intCtr++) {
int intUseridI = cursorFollowing.getColumnIndex(Tables.FollowTable.COLUMN_USERID);
String strUseridI = cursorFollowing.getString(intUseridI);
Cursor cursorFollowingFeed = DataAdapters.getFeed(dbHelper, strUseridI);
\\This code is for retrieving the posts of the people the user follows.
if(intCtr>0)
{
mergeCursor = new MergeCursor(new Cursor[]{mergeCursor, cursorFollowingFeed});
}else {
mergeCursor = new MergeCursor(new Cursor[]{cursorFeed, cursorFollowingFeed});
}
//This code is for merging the cursors.
if (intCtr + 1 == cursorFollowing.getCount()) {
cursorFollowing.close();
} else {
cursorFollowing.moveToNext();
}
}
ListViewAdapterMeasurement adapterMeasurement = new ListViewAdapterMeasurement(this, mergeCursor);
listviewFeed.setAdapter(adapterMeasurement);
}else
{
ListViewAdapterMeasurement adapterMeasurement = new ListViewAdapterMeasurement(this, cursorFeed);
listviewFeed.setAdapter(adapterMeasurement);
}
It is all working well. I just want to order the posts by Date or by ID.
Is there any way to sort MergeCursor?
I guess there is no way to sort the MergeCursor so I tried to think of other ways.
I changed my query like this:
public static Cursor getFeed (DBHelper dbHelper, String strUserID)
{
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursorFollowing = getFollowing(dbHelper,strUserID);
String strQuery = "SELECT * FROM feed_tbl WHERE "+ Tables.FeedTable.COLUMN_USERID + "="+strUserID;
if(cursorFollowing.getCount()>0)
{
for(int intCtr=0;intCtr<cursorFollowing.getCount();intCtr++)
{
int intUseridI = cursorFollowing.getColumnIndex(Tables.FollowTable.COLUMN_USERID);
String strUseridI = cursorFollowing.getString(intUseridI);
String strConcatQuery = " OR "+ Tables.FeedTable.COLUMN_USERID + "="+strUseridI;
if (intCtr + 1 == cursorFollowing.getCount()) {
cursorFollowing.close();
} else {
cursorFollowing.moveToNext();
}
strQuery = strQuery +""+strConcatQuery;
Log.v(TAG,strQuery);
}
}
Cursor cursor = db.rawQuery(strQuery+" ORDER BY "+ Tables.FeedTable.COLUMN_ID + " DESC",null);
if (cursor != null) {
cursor.moveToFirst();
}
return cursor;
}
The result on the log tag is this:
V/FeedMe: SELECT * FROM feed_tbl WHERE feed_userid=1 OR feed_userid=2 OR feed_userid=4 OR feed_userid=5
Just to make things clear to those who have the same problem with me which is about MergeCursor sorting. THERE IS NO SUCH WAY TO SORT MERGECURSOR :)
Thankyou!
I have managed to obtain the data from the SQLite database and plot the actual values for the cube line chart view. Since the values should be plotted against the date which is also stored in the database in the format (dd-mm-yyyy) and is expected to be plotted on the x, I have difficulties how to actually realize that. Current parts of my code like this:
hapinessdb.class (SQLite database and the code to search for the date entries)
/**
* Return the date as a string and place it to ArrayList
* of strings.
* #return
*/
public ArrayList<String> getDataForDate() {
ArrayList<String> dateValues = new ArrayList<String>();
String[] columns = new String[]{KEY_ROWID, KEY_DATE, KEY_TIME,KEY_HAPPY,
KEY_NORMAL, KEY_SAD };
Cursor c = ourDatabase.query(DATABASE_TABLE, columns, null,
null, null, null, null); //Read the data from columns with cursor
String result = "";
int iDate= c.getColumnIndex(KEY_DATE);
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
result = c.getString(iDate);
dateValues.add(result);
}
return dateValues;
}
chart.class (method to obtain the ArrayList of date strings from happinessDb.class)
ArrayList<String> dateList = new ArrayList<String>();
private void dateDataFromDb() {
happinessDb dateInfo = new happinessDb(this);
try {
dateInfo.open();
} catch (SQLException e) {
e.printStackTrace();
}
ArrayList<String> data = dateInfo.getDataForDate();
dateInfo.close();
dateList = data; // Adds the dates to the String arrayList
My question is aimed towards, how to break the ArrayList of Strings and use them as part of the X-Axis series together with the bellow addHappyData method which plots the Integer values from ArralyList of Integers.
private void addHappyData() {
Integer x = 0;
for (Integer happy : happyList) {
mCurrentSeries.add(x += 20, happy);
}
}
Thank you for your time,review, possible review and answer on this post,
brg
Animus
I've been working with SQLite on android and I would like to add an arraylist to a column in a table, and then fetch the data back as an arraylist. The arraylist is a list of Longs. I've noticed that SQL has an option for storing BLOBS, however it looks like I need to convert the arraylist to a byte[] first before being able to store it as a blob in my SQLite database.
If anyone has a solution on how to save arraylists into an SQLite database that would be greatly appreciated. Or is there any other option for saving my array of data, i should consider?
To Insert :
ArrayList<String> inputArray=new ArrayList<String>();
//....Add Values to inputArray
Gson gson = new Gson();
String inputString= gson.toJson(inputArray);
System.out.println("inputString= " + inputString);
use "inputString" to save the value of ArrayList in SQLite Database
To retreive:
Get the String from the SQLiteDatabse what you saved and changed into ArrayList type like below:
outputarray is a String which is get from SQLiteDatabase for this example.
Type type = new TypeToken<ArrayList<String>>() {}.getType();
ArrayList<String> finalOutputString = gson.fromJson(outputarray, type);
In SQLite use text as format to store the string Value.....
Please forgive me for savagely plagiarizing my previous answer to BLOB vs. VARCHAR for storing arrays in a MySQL table. The other answers over there are also very pertinent.
I think Con's approach is probably better than using java serialization since java's builtin serialization will need additional bytes, and non-java applications will have a harder time dealing with the data.
public static void storeInDB(ArrayList<Long> longs) throws IOException, SQLException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
for (long l : longs) {
dout.writeLong(l);
}
dout.close();
byte[] asBytes = bout.toByteArray();
PreparedStatement stmt = null; // however you get this...
stmt.setBytes(1, asBytes);
stmt.executeUpdate();
stmt.close();
}
public static ArrayList<Long> readFromDB() throws IOException, SQLException {
ArrayList<Long> longs = new ArrayList<Long>();
ResultSet rs = null; // however you get this...
while (rs.next()) {
byte[] asBytes = rs.getBytes("myLongs");
ByteArrayInputStream bin = new ByteArrayInputStream(asBytes);
DataInputStream din = new DataInputStream(bin);
for (int i = 0; i < asBytes.length/8; i++) {
longs.add(din.readLong());
}
return longs;
}
}
Note: If your lists will sometimes contain more than 31 longs (248 bytes), then you'll need to use BLOB. You cannot use BINARY() or VARBINARY() in MySQL. I realize you're asking about SQLite, but in the spirit of completely plagiarizing my previous answer, I will pretend you're asking about MySQL:
mysql> CREATE TABLE t (a VARBINARY(2400)) ;
ERROR 1074 (42000): Column length too big for column 'a' (max = 255);
use BLOB or TEXT instead
I had two ArrayList<String>, both will 1000+ entries. I looked at blobs and bytes, but for me the solution to speeding up the process and making it usable was by changing the insert method and getting rid of database.insert - Credit for this is here.
private static final String INSERT = "insert into "
+ YOUR_TABLE_NAME+ " (" + COLUMN_1 + ", "
+ COLUMN_2 + ") values (?, ?)";
public void insertArrayData(ArrayList<String> array1,
ArrayList<String> array2) {
try {
database.open();
} catch (SQLException e) {
e.printStackTrace();
}
int aSize = array1.size();
database.beginTransaction();
try {
SQLiteStatement insert = database.compileStatement(INSERT);
for (int i = 0; i < aSize; i++) {
insert.bindString(1, array1.get(i));
insert.bindString(2, array2.get(i));
insert.executeInsert();
}
database.setTransactionSuccessful();
} catch (SQLException e) {
e.printStackTrace();
} finally {
database.endTransaction();
}
try {
database.close();
} catch (Exception e) {
e.printStackTrace();
}
}
It's easily adaptable to Longs and Integers etc and lightening quick. So thankfully I didn't have to scratch my head any longer about blobs and bytes! Hope it helps.
There is an easier way that do such thing in completely another way.
you can make an string that consists of all your array values.
for that make an StringBuilder and append the values continuously and offcource with a separator (like a simbole you which you won't use in your array values . for example virtual '|' .
in code for example :
double[] mylist = new double[]{23, 554, 55};
StringBuilder str = null;
str = new StringBuilder();
for(int i=0;i<mylist.length;i++){
str.append(mylist[i]+"|");
}
String result = str.toString();
db.open();
db.insert(result);
db.close();
when you want to fetch them and use the values. get the column from database pure it to String and with the splite() opration pure each values of array in a column of array than u can easily use it :)
lets do it in code :
String str = db.getdata();
String[] list = str.split("|");
with a simple convert you can use them as double;
double mydouble = Double.parsDouble(list[1].toString());
maybe it is not standard but it is helpfull, hope help ;)
Sounds like you want to serialize the List. Here is a tutorial/intro to the Java Serialization API.
You'll have to do it manually, go through each item in the list and change it to byte before storing it in the database
for (long l : array<long>){
//change to byte here
//Store in database here
}
ArrayList<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
String joined = TextUtils.join(",", list);
Log.i(TAG, "joined strings: " + joined);
String[] array = TextUtils.split(joined, ",");
Log.i(TAG, "joined strings: " + array[0] + array[1] + array[2]);