Optimize Large MySQL ResultSet - java

I am running a query against a database, and it works fine, but building the data to place into a table takes a while. I am retrieving 500,000 rows of data, and I don't think that it should take very long to display that, but my Application stops responding because of how long it takes. I have tried the same query in different applications and they load the really fast. Is there anything I can do to speed mine up?
public ArrayList<HashMap<String, ArrayList>> runQueries(String query){
ArrayList<HashMap<String, ArrayList>> arrayList = new ArrayList<>();
try{
Statement stmt = conn.createStatement();
stmt.execute(query);
while(true){
HashMap<String, ArrayList> hm = new HashMap<>();
// Get next resultset if no resultset was returned.
// The query was either an insert, update or delete
if(stmt.getUpdateCount() > -1){
stmt.getMoreResults();
continue;
}
// If the resultset is null exit
if(stmt.getResultSet() == null){
break;
}
// We have a resultset!
// Save the columns to an array
// We can then display them later
ResultSet rs = stmt.getResultSet();
int numColumns = rs.getMetaData().getColumnCount();
ArrayList<String> columns = new ArrayList();
for(int i = 0; i < numColumns; i++){
columns.add(rs.getMetaData().getColumnName(i + 1));
}
// Save the columns to the hashmap
hm.put("columns", columns);
// We now need to save the rows to an array as well
// We can then display them later as well
ObservableList<ObservableList<String>> oblist = FXCollections.observableArrayList();
while(rs.next()){
ObservableList<String> row = FXCollections.observableArrayList();
for(int i = 1; i <= numColumns; i++){
row.add(rs.getString(i));
}
oblist.add(row);
}
ArrayList<ObservableList> rows = new ArrayList<>();
rows.add(oblist);
rs.close();
// Save the rows to the hashmap
hm.put("rows", rows);
// Save the hashmap to the final array
arrayList.add(hm);
stmt.getMoreResults();
}
}catch(SQLException ex){
Logger.getLogger(Mysql.class.getName()).log(Level.SEVERE, null, ex);
}
return arrayList;
}
Edit:
I have narrowed it down to the section that takes a while to run:
ObservableList<ObservableList<String>> oblist = FXCollections.observableArrayList();
while(rs.next()){
ObservableList<String> row = FXCollections.observableArrayList();
for(int i = 1; i <= numColumns; i++){
row.add(rs.getString(i));
}
oblist.add(row);
}

First: Do not load all records. Why would you even do that? You could implement some kind of pagination in your application.
I.e. Add two simple buttons below your table "Previous" and "Next" and "Search" field above. In beginning, load i.e. 100 records. Upon each click, load next 100 records etc. If user is looking for specific record, he could use "Search" field.
Second: Load your records in background thread to keep your application UI always responsive and prevent freezing. Read more about concurrency in JavaFX.

Try replacing
if(stmt.getUpdateCount() > -1){
stmt.getMoreResults();
continue;
}
if(stmt.getResultSet() == null){
break;
}
with
if(stmt.getUpdateCount() > -1){
if (!stmt.getMoreResults()) {
break ;
}
}

Related

Populate jTable from ArrayList<String>, without creating a new class.

I have a Web Service which returns ArrayList<String> from Database. I need to populate a jTable on client Java application with that ArrayList. How can I organise the ArrayList, so it shows in correct rows on the table? Currently it returns every element, separated with comma.
Here is the WebMethod bit:
#WebMethod(operationName = "ListCustomers")
public ArrayList ListCustomers() {
try{
Class.forName("org.apache.derby.jdbc.ClientDriver");
Connection con = DriverManager.getConnection("jdbc:derby://localhost:1527/BankDB", "bankadmin", "bankadmin");
Statement st = con.createStatement();
PreparedStatement prst = con.prepareStatement("select Name, AccountNumber from CUSTOMERS");
ArrayList<String> list = new ArrayList<>();
ResultSet rs = prst.executeQuery();
while(rs.next()){
String nm = rs.getString("Name");
String an = rs.getString("AccountNumber").toString();
list.add(new String (nm));
list.add(new String (an));
}
return (ArrayList) list;
}
catch(SQLException ex){
System.err.println(ex.getMessage());
return null;
}
catch (ClassNotFoundException ex) {
Logger.getLogger(BankServerService.class.getName()).log(Level.SEVERE, null, ex);
System.out.println("second");
return null;
}
finally{
if(st != null){
try {
st.close();
}
catch(SQLException ex){
System.out.println("Could not close statement");
}
}
}
}
This returns:
[TestName1, 46484654897, Name2, 646543543, emp3, 534354354]
The client node looks like this, which I run in another method:
listCustomers();
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
Object rowData[] = new Object[listCustomers().size()];
for (int i=0; i<listCustomers().size(); i++){
rowData[i] = listCustomers().get(i);
model.addRow(rowData);
}
This populates only TestName1, 46484654897, into like 10 rows.
I need to show TestName1, Name2, emp3, on separate rows on 1st column of the table, and 46484654897, 646543543, 534354354 on separate rows on 2nd column.
If your goal is to put each name/account number combination on it's own row, but you've got them all in a single list you would need to do something a bit different.
listCustomers();
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
for(int i = 0; i < listCustomers.size(); i+=2){
Object[] rowdata = new Object[] {listCustomers.get(i), listCustomers.get(i+1)};
model.addRow(rowdata);
}
You increment i by 2 each time, since you only care about name + account number combo, and it goes in pairs.
Secondly you only want to pass model.addRow the object array with the data you want for that row. You were adding every string in listcustomer to it, then adding the whole array to the model as a row.
Above I just added a new rowdata object array inside the for loop, and populated it with the value at i (the name) and the value at i+1 (the account number). Keep in mind this will get out of sync if for some reason a name or account number is missing.

How to get the first 2 records from Access's Table and Add them to Jtable (Data filter)

String[] Titulo = {
"ID",
"CDEP",
"NOMDEP",
"POB"
}; //This is the head of the Jtable
Mnc2 = new DefaultTableModel(null, Titulo);
String[] Filas = new String[4];
try { //HERE IS THE CONECTION TO THE BDD(ACCESS)
Conexion_DDB Conn = new Conexion_DDB();
//Prepared Query
PreparedStatement Consulta = Conn.getConnection().prepareStatement("SELECT * FROM [DEPARTAMENTOS]");
ResultSet Resultadobm = Consulta.executeQuery();
while (Resultadobm.next()) {
//This is the part thath I don't know how to get the Strings or values for each row
Filas[0] = Resultadobm.getString("CDEPID");
Filas[1] = Resultadobm.getString("CDEP");
Filas[2] = Resultadobm.getString("POBDEP");
Filas[3] = Resultadobm.getString("NOMDEP");
//There are more records but just for example
while (Resultadobm.next()) {
//This is the part thath I don't know how to get the Strings or values for the first two rows
Mnc2.addColumn(Filas);//Add the columns
}
/*At this part, I'm trying to get the values equals or higher than 50,000 to show then in the same table at the same time with the two first records found*/
//If you can help me here too I would appreciate it a lot
int Menora = 50000;
if (Mnc2.getValueAt(1, 1).equals(Menora)) {
Tabla_Busquedas.setValueAt(Mnc2, 1, 1);
}
Conn.Desconexion(); //Close the connection
} catch (SQLException ex) {
Logger.getLogger(Form_Tabla.class.getName()).log(Level.SEVERE, null, ex);
}
}
while (Resultadobm.next()) { //This is the part thath I don't know how
to get the Strings or values for the first two rows
I am not sure I understand right.I think you can terminate the loop after you fetched first two rows:
while (Resultadobm.next() && Resultadobm.getRow() <= 2) {
// ...your code
}
You can go to the source:
"SELECT TOP 2 * FROM [DEPARTAMENTOS] ORDER BY [SomeUniqueField]");

Java ArrayIndexOutOfBounds Exception -

I have 2 panels in my form which bound to a parent panel which has its layout set to card layout.
In the JFrame's constructor, a database operation is performed and data is taken and used to fill a JList.
Then when a user clicks on an item, a new panel ( as part of CardLayout ) shows up with a JTable filled with data taken from a database according to the selection made by the user in the listbox .
The program is basically a clinic management system.
In this JFrame, the cashier can see the id numbers of patients who are to pay their bills . These id numbers are displayed as a list in the JList.
On clicking the list, a patient id is selected and then the drugs prescribed are queried from the database and displayed in the table along with drug id & price.
The problem is that I get an ArrayOutOfBoundsException whenever I try to add a row to the JTable.
These are declared outside the mouseclick event (globally)
private Connection medDbConn = Connect("medicines.db");
private PreparedStatement mPst = null;
private ResultSet mRs = null;
private Connection conn = Connect("bills.db");
private PreparedStatement pst = null;
private ResultSet rs = null;
private ArrayList medicines_fine = new ArrayList();
private ArrayList medicines_fine_name = new ArrayList();
private ArrayList medicines_fine_qty = new ArrayList();
private ArrayList medicines_id = new ArrayList();
private ArrayList medicines_price = new ArrayList();
private ArrayList medicines_subtotal = new ArrayList();
private Double cf = 0.00;
/**
* Creates new form ViewBills
*/
private DefaultListModel listmodel;
private DefaultListModel model;
The following is executed in the mouseclick event of the JList .
// GET SELECTED VALUE FROM LIST
String listvalue = (String) jList1.getSelectedValue();
// SPLIT THE SELECTED LIST VALUE INTO ID AND DATETIME
String[] datas = listvalue.split("=");
// datas[0] = id and datas[1] = datetime
// STORE PATIENTID and DATETIME
String Patient_Id = datas[0].trim();
String DateTime = datas[1].trim();
String medicines_raw = null;
String[] medicines;
String[] keypair;
try {
// CONNECT THE DATABASE & PREPARE IT . THEN SET THE VALUES & FURTHER EXECUTE QUERY TO GET RESULT INTO "rs"
pst = conn.prepareStatement("SELECT CONSULTFEE,MEDICINES FROM VIEWBILLS WHERE ID=? AND DATETIME=?");
pst.setString(1, Patient_Id);
pst.setString(2, DateTime);
rs = pst.executeQuery();
// IF THERE IS A RECORD , THEN DO SOMETHING
if (rs.next()) {
cf = rs.getDouble("consultfee");
// GET THE RAW VALUE IN THE FORMAT Aspirin=12,sdsd=1,asdasd=2
medicines_raw = rs.getString("medicines");
// SPLIT THE WORD BY COMMAS AND STORE INTO ARRAY , SO EACH INDEX has Aspirin=12 kind of values
medicines = medicines_raw.split(",");
for (int i = 0; i < medicines.length; i++) {
medicines_fine.add(medicines[i]);
}
for (int i = 0; i < medicines_fine.size(); i++) {
keypair = medicines_fine.get(i).toString().split("=");
medicines_fine_name.add(keypair[0]);
System.out.println(keypair[0]);
System.out.println(keypair[1]);
medicines_fine_qty.add(keypair[1]);
}
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e);
}
CardLayout cl = (CardLayout) Parent.getLayout();
cl.show(Parent, "card3");
// SECOND DATABASE CONNECTION STARTS HERE ...
for (int i = 0; i < medicines_fine_name.size(); i++) {
try {
pst = medDbConn.prepareStatement("SELECT ID,SELLPRICE FROM MEDICINES WHERE NAME=?");
pst.setString(1, (String) medicines_fine_name.get(i));
rs = pst.executeQuery();
if (rs.next()) {
medicines_id.add(rs.getString("id"));
medicines_price.add(rs.getDouble("sellprice"));
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e);
}
for ( int i = 0; i < medicines_fine_name.size() ; i++) {
tblmodel.addRow(new Object[] {medicines_id.get(i),medicines_fine_name.get(i),medicines_price.get(i),medicines_fine_qty.get(i)});
}
}
The whole thing sometimes run without error if the last for loop is removed . ( but that beats the purpose )
And Yeah , I said "sometimes" .. I am very much confused and as you can see, this code is written in a very ugly manner. I had written it the normal way, but this ArrayIndexOutOfBoundsException made me re-write the whole code 3 time, which ended in me writing pretty bad & ugly code but with the error still hanging on.
Pls let me know if anything is still unclear
Edit1: Basically what i'm trying to do is from the selected string in jlist , separate the datetime part and id part using split() function and store them to an array . Then use this id and name to select medicines from database . "medicines" is a string containing comma separated values like Aspirin=2,Amoxylin=5,etc=2,etc=10 where =10 means 10 of them ( indicating quantity . Then the drug name is stored into an arraylist and qty into another . Then use this medicine_name to get the medicine_id and sellprice. Finally set all this into a table .
The problem is here:
for ( int i = 0; i < medicines_fine_name.size() ; i++) {
tblmodel.addRow(new Object[] {medicines_id.get(i),medicines_fine_name.get(i),medicines_price.get(i),medicines_fine_qty.get(i)});
}
You are iterating over index in arraylist where there are no elements present.
What you are doing is you are firing two queries, in first query say you got 10 records.
Now in second query you iterate over your 10 records (processed and passed in arrayList from first query result) and fire in query 10 times. You may or may not get record and you do:
if (rs.next()) {
medicines_id.add(rs.getString("id"));
medicines_price.add(rs.getDouble("sellprice"));
}
if out of 10 you get record for 5 then size of medicines_id, medicines_price arraylist would be just 5.
Now comes your final loop as above. Here in the for loop you try to iterate over medicines_id,medicines_fine_name,medicines_price,medicines_fine_qty until size of 10 as per first query (medicines_fine_name.size()).
so when your i goes to index 6, you try to get on say
medicines_id.get(6)
but this element you never populated as per your second query hence ArrayIndexOutOfBoundException.
To fix this, one way would be to use else part to populate dummy value to id and price like below:
if (rs.next()) {
medicines_id.add(rs.getString("id"));
medicines_price.add(rs.getDouble("sellprice"));
} else {
medicines_id.add(rs.getString("9999"));
medicines_price.add(rs.getDouble("10.00"));
}

JTable doesn't show the newly inserted row/data

I have a JTable and I populte the table as follows:
jTable_Std_info.setModel(DBControler.getALLStudents());
And the following is a static method in a class named DBControler which retrieves all the data from the database(Oracle).
public static DefaultTableModel getALLStudents() throws SQLException, Exception {
DefaultTableModel tableModel = new DefaultTableModel();
Vector rows = new Vector();
Vector columns = new Vector();
try {
conn = geConnection();
cst = conn.prepareCall("{? = call std_getInfoFunc}");
cst.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR);
cst.execute();
res = (ResultSet) cst.getObject(1);
System.out.print(res);
ResultSetMetaData rsm = res.getMetaData();
for (int i = 1; i <= rsm.getColumnCount(); i++) {
columns.addElement(rsm.getColumnName(i));
}
int row = 0;
while (res.next()) {
Vector vRow = new Vector(); //to store the current row
//System.out.println("Row " +row+"\n");
for (int i = 1; i <= rsm.getColumnCount(); i++) {
String columnValue = res.getString(i);
vRow.addElement(columnValue);
}
row += 1;
rows.addElement(vRow);
}
tableModel.setDataVector(rows, columns);
} catch (SQLException e) {
e.printStackTrace();
} finally {
res.close();
conn.close();
}
return tableModel;
}
So far everything works fine, but the problem is that if I insert a new record in the database, the JTable doesn't get the newly inserted row/data. Why is that and how can I fix this problem?
UPDATE:
It's retrieving the data when I commit my new insertion. So do I have to commit each time I update? Or is there any other ways to do this?
I think that you looking for Oracle Built-In Database Change Notification, not sure if is accesible for Oracle's in free-versions, if not then never mind, for MySQL is there two or three similair API for Java JDBC
But the problem is that if I insert a new record in the database, the JTable doesn't get the newly inserted row/data. Why is that?
The TableModel doesn't know when the database is updated.
and how can I fix this problem?
If your application is adding the row to the database then it also needs to add a row to the TableModel at the same time.

Populate Java table with resultSet

I'm building a java application that gets its data from an oracle database and puts it into a JTable.
My problem is I am not able to populate the table, I don't understand how to do it. Javadoc is useless.
I don't understand why the table doesn't get the rows:
if ((report.getMsg()=="selectEventoAll") && (report.getEsito()==1))
{
DefaultTableModel dtm = new DefaultTableModel();
eventi_tb.setModel(dtm);
try
{
ResultSet res_eventi = report.getRes();
i = 0;
Object[][] datiEventi = new Object[report.getRowCount()][5];
while(res_eventi.next())
{
j = 0;
while (j < 5)
{
datiEventi[i][j] = res_eventi.getObject(j+2);
j++;
}
dtm.addRow(datiEventi[i]);
i++;
}
}
You can do this using a custom implementation of AbstractTableModel.
After you get your results back, put them in a list and let this be the backing list for your table model.
See here .. http://download.oracle.com/javase/tutorial/uiswing/components/table.html#data
Table From Database should get you started.

Categories

Resources