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"));
}
Related
I'm trying to filter a JTable but the results are not as expected.
Below is the JTable with the added elements (I'm using MySQL to store the Data)
JTable with contents - Picture
When I try to filter the list for someone specific, I do not get the data from the table. For example, I search for "Ana" and nothing appears.
Search results for "Ana" - Picture
If I try to search using some "numbers", like the salary, I get the right result but the ID is not right. Pictures to clarify the issue below.
Wrong ID
Right ID
The Code to generate the ArrayList with the employees :
public static ArrayList<Angajat> listaAngajati() {
ArrayList<Angajat> listaAngajati = new ArrayList<>();
try (java.sql.Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/angajati", "root", "***");) {
Statement st = conn.createStatement();
st.executeQuery("select * from angajati");
ResultSet rs = st.getResultSet();
Angajat angajat;
while (rs.next()) {
angajat = new Angajat(rs.getInt("id"), rs.getString("nume"), rs.getString("prenume"), rs.getInt("varsta"), rs.getString("adresa"), rs.getDouble("salariu"));
listaAngajati.add(angajat);
}
} catch (SQLException ex) {
System.out.println("Error in database connection: \n" + ex.getMessage());
}
return listaAngajati;
}
public static void arataAngajati() {
ArrayList<Angajat> arataAngajati = listaAngajati();
DefaultTableModel model = (DefaultTableModel) tabelangajati.getModel();
Object[] rand = new Object[6];
for (int i = 0; i < arataAngajati.size(); i++) {
rand[0] = arataAngajati.get(i).getID();
rand[1] = arataAngajati.get(i).getNume();
rand[2] = arataAngajati.get(i).getPrenume();
rand[3] = arataAngajati.get(i).getVarsta();
rand[4] = arataAngajati.get(i).getAdresa();
rand[5] = arataAngajati.get(i).getSalariu();
model.addRow(rand);
}
}
Code to filter the JTable
private void cautaInTabelKeyReleased(java.awt.event.KeyEvent evt) {
DefaultTableModel tabel = (DefaultTableModel) tabelangajati.getModel();
String query = cautaInTabel.getText().toLowerCase();
TableRowSorter<DefaultTableModel> sort = new TableRowSorter<DefaultTableModel>(tabel);
tabelangajati.setRowSorter(sort);
sort.setRowFilter(RowFilter.regexFilter(query));
}
Question : How can I modify the code so when I try to search for a an employee using his name to get the right result (not like now - no results) and when trying to modify the employee data, to get the right ID as shown in the JTable (example in the pictures above) ?
EDIT
In order to filter the data from the table accordingly I had to use
sort.setRowFilter(RowFilter.regexFilter("(?i)" + query));
When I was filtering the table, only the view modified and not the values from the row (Even if I saw the values from the row 3 and values on the backend where from the row 1). I managed to modify the following row and the table works perfectly.
From :
int row = tabelangajati.getSelectedRow();
To :
int row = tabelangajati.convertRowIndexToModel(tabelangajati.getSelectedRow());
I have a button in which after pressing it, the items that are in critical level will populate the jTable. I have managed to do it but the issue i'm facing is that the row it adds just populates the first column and leave the rest blank. My questions are, how come it only populates the first column and how do I get the rest of the columns filled?
PS. The table columns are PRODUCT NAME - STOCK ON HAND - MINIMUM QTY
yet, the first column shows the stockOnHand not even the productName.
Below are my codes for the button:
DefaultTableModel model = (DefaultTableModel)tableMinimum.getModel();
try{
String viewCriticalSQL = "Select * from tblProduct";
pstmt = conn.prepareStatement(viewCriticalSQL);
rs = pstmt.executeQuery();
while(model.getRowCount()>0){
model.removeRow(0);
}
while(rs.next()){
String prodName = rs.getString("productName");
int getStock = rs.getInt("stockOnHand");
int getMinimum = rs.getInt("minimumOrder");
if (getStock<getMinimum){
Object[] adRow = {prodName, getStock, getMinimum};
model.addRow(adRow);
}
}
}catch(SQLException e){
JOptionPane.showMessageDialog(this, e.getMessage());
}
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.
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]");
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 ;
}
}