Elements disappearing from an ArrayList - java

I'm having some troubles with this code:
ArrayList<Shop> listShops = new ArrayList<Shop>();
Shop currShop = new Shop();
String query = "SELECT * FROM Shop WHERE... ";
try {
PreparedStatement statement = connection.prepareStatement(query);
ResultSet rs = statement.executeQuery();
while(rs.next()) {
currShop.setName(rs.getString(1));
currShop.setDescription(rs.getString(2));
System.out.println(listShops.add(currShop));
}
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("List size: "+listShop.getSize());
for(Shop s: listShop) {
System.out.println(s.getName());
}
Output:
true
true
true
[...]
List size: 78
[78 empty strings]
And I'm not understanding why it is giving me those empty lines. I'm 100% sure that currShop works fine because I printed currShop.getName() and currShop.getDescription() (in the rs.next() while) and they both work. It also gives me "true" booleans indicating a successful insert into the ArrayList, so why doesn't it print anything?

You should create a new Shop object every time inside the while loop:
while(rs.next()) {
Shop currShop = new Shop();
currShop.setName(rs.getString(1));
currShop.setDescription(rs.getString(2));
System.out.println(listShops.add(currShop));
}
Otherwise, you are just adding a single Shop instance many times to the list, overwriting the name and description on each iteration.

Wim is right you must create a new Object inside the loop. Otherwise there is no point in overwriting the same old object again and again.
By using inside your while loop
Shop currShop = new Shop();
will be created a new instance everytime and the listShops.add(currShop) will have add new item on each insert.
This kind of mistake happens in the beginning, no worries keep learning !

Related

update the picture in database

Hello! I am creating an app for students database. Recently I got a problem when I want to change data that is related to the specific student. Especially problems occur when I want to change the picture for the specific student. I need to check if the picture belongs to the student or not. I store my pictures to the folder if I change the picture, I delete previous one and create a new one. My question about how to check if a picture belongs to the specific student?
I check students in this way.
// get the name of the student from first table getValueTableName
// get the name of the picture from first table getValueTablePicture
getValueTableName = jTable1.getModel()
.getValueAt(jTable1.getSelectedRow(), 0).toString();
getValueTablePicture = jTable1.getModel()
.getValueAt(jTable1.getSelectedRow(), 3).toString();
File sourceFile = new File(getValueTablePicture);
setPicture = sourceFile.getName();
// GET NAME OF THE STUDENT AND THE PICTURE FROM DATABASE AND COMPARE
// THEM TO THE CURRENT USER
try {
CallableStatement statement = null;
Connection data = getmyConnection();
statement = data.prepareCall("{call editStudentByName}");
myResults = statement.executeQuery();
while (myResults.next()) {
// COPY PATH IN getEditName
getEditName = myResults.getString("Name");
// COPY PATH IN getEditPicture
getEditPicture = myResults.getString("Picture");
// add students from database to array
// mylist.add(getEditName.concat(getEditPicture));
mylist.add("\n");
}
myResults.close();
} catch (Exception c) {
c.printStackTrace();
}
// I don't know how to move from this point when I check names with loop
// I check the student with the loop
for (String person : mylist) {
if (getValueTableName.concat(sourceFile.getName()).equals(person) == true) {
}
System.out.print(getValueTableName.concat(sourceFile.getName())
.equals(person));
errors.append(
"- Please choose another picture or rename it!\n Picture ")
.append(getEditPicture)
.append(" is exist for a student " + getEditName)
.append("\n");
jTextField3.requestFocusInWindow();
jTextField3.setText("");
}
The very first thing I'd do is not using separate Strings with strange names like getEditName - this is confusing. Consider having POJO (Student.class) and working with that
So you want to replace single student picture? Why do you have to iterate some array in this case? You should get single student from database (by Id or via some unique set of attributes).
Ok, lets say you have a list of students and you iterate over it. But you still have to change picture for single person so that there is no need to check.
Simply do
String pictureFileName = person.getPicture();//assming getPicture() method returns current picture path
and then save new picture with the same name. In this case old picture will be overwritten so that no issue with checks.
UPD:
If you want to check for picture existence you can do the same:
String pictureFileName = person.getPicture();
File f = new File(pictureFileName );
if(f.exists() && !f.isDirectory()) {
// do something, say report warning
}
UPD:
If you don't require an ability for students to share same file as a picture it's better to implement this at DB level as well via this https://www.w3schools.com/sql/sql_unique.asp - so that you simply won't be able to write two different student records with the same picture path field. In this case checks won't matter anymore and you can simply overwrite the picture file because it belongs to single student only
Finally, I got my things done. It was a very painful moment, but I got what I wanted. The problem was hidden in a column "Picture" in the table "Student" of my database. First, I add UNIQUE constraint ensures to my column that all values in a column are different. Second, I created two stored procedures:
CREATE DEFINER=`root`#`localhost` PROCEDURE `checkStudentByPicture`(
in picture_name varchar(100)
)
BEGIN
SELECT COUNT(*) FROM students_center.Student st WHERE st.Picture = picture_name;
END
The first procedure checks if my column has unique names and doesn't allow to add the same name to the column.
And I created a second one:
CREATE DEFINER=`root`#`localhost` PROCEDURE `getStudentNameByPicture`(
in name varchar(45),
in pic_name varchar(100)
)
BEGIN
SELECT COUNT(*) FROM students_center.Student st WHERE st.Name = name and st.Picture=pic_name;
END
The second procedure checks if the column "Picture" is related to the column "Name". If the column "Picture" is not related, the user doesn't allow to change the name.
Here is the code about checking if my data related to context:
private boolean validateFieldEditStudent() {
StringBuilder errors = new StringBuilder();
// call stored procedure checkStudentByPicture
File sourceFile = new File(jTextField3.getText());
String checkStudentName=jTable2.getValueAt(jTable2.getSelectedRow(), 0).toString();
try {
CallableStatement statement = null;
Connection data = getmyConnection();
statement = data.prepareCall("{call checkStudentByPicture(?)}");
statement.setString(1, sourceFile.getName());
myResults = statement.executeQuery();
while (myResults.next()) {
//COPY PATH IN pictureName
getPictureCount = myResults.getInt(1);
}
myResults.close();
} catch (Exception c) {
c.printStackTrace();
}
}
// call stored procedure checkStudentByPicture
try {
CallableStatement statement = null;
Connection data = getmyConnection();
statement = data.prepareCall("{call getStudentNameByPicture(?, ?)}");
statement.setString(1, checkStudentName);
statement.setString(2, sourceFile.getName());
myResults = statement.executeQuery();
while (myResults.next()) {
//COPY PATH IN pictureName
getStudentNameCount = myResults.getInt(1);
}
myResults.close();
} catch (Exception c) {
c.printStackTrace();
}
//check if data is related to the specific user
if(getFileChooserCount > 0) {
if(getStudentNameCount != 1) {
if(getPictureCount == 1) {
errors.append("- Picture "+sourceFile.getName()+" existed in the database!\n");
jTextField3.setText("");
jTextField3.requestFocusInWindow();
}
}
}
if (errors.length() > 0) {
JOptionPane.showMessageDialog(EditStudent, errors, "Warning!", JOptionPane.ERROR_MESSAGE);
return false;
}
return true;
}

Populate List<Object> from ResultSet without creating new Object for each dataset

I have been always using the following code to add Objects from a ResultSet into a List. However someone commented that it is not very efficient to create a new Object for every dataset in the ResultSet. Is there a better way? Or, is there a whole different way to add Object from a ResultSet to a List?
public static List<Students> selectFromDatabase(Connection conn, Statement stmt){
List<Students> list = new ArrayList<Students>();
String select = "SELECT * FROM students";
ResultSet rs = null;
try{
stmt = conn.createStatement();
rs = stmt.executeQuery(select);
while(rs.next()){
//you have to create a new OBJECT FOR EACH LOOP
Students student = new Students();
student.setStudentId(rs.getInt(1));
student.setName(rs.getString(2));
student.setGpa(rs.getInt(3));
list.add(student);
}
}catch(SQLException e){
System.out.println(e.getMessage());
}
return list;
}
The comments on your OP have pretty much already answered your question, I'll just provide some extra insight.
You have a List that contains Students objects. You populate this List by creating a Students object and adding that object to the List for every result that's in rs. How would you go about doing this if you just made one Students object? You've gotta make as many objects as there are results in rs in order to do what it is you're trying to do. This is pretty much what always happens with these data structures, unless it's an array.
that someone is right at some cost of heavy coding. It is not really needed to create a new object . you can always use the same student object and use set to new values and add to list. Sorry I was about to same object with different references....! Copy pasted and missed to add the code inside the loop ! The edited is was i meant
public static List<Students> selectFromDatabase(Connection conn, Statement stmt){
List<Students> list = new ArrayList<Students>();
String select = "SELECT * FROM students";
ResultSet rs = null;
try{
stmt = conn.createStatement();
rs = stmt.executeQuery(select);
Students student = null;
while(rs.next()){
student = new Students();
//you have to create a new OBJECT FOR EACH LOOP
student.setStudentId(rs.getInt(1));
student.setName(rs.getString(2));
student.setGpa(rs.getInt(3));
list.add(student);
}
}catch(SQLException e){
System.out.println(e.getMessage());
}
return list;
}

Every String[] within an ArrayList has the same value

I am trying to create Oracle SQL scripts for creating/replacing several views by using information acquired from the data dictionary.
I already managed to set up a working database connection via JDBC drivers and a query to the data dictionary, which works as well and returns the correct values.
However upon storing the queried information in a String array - which is then added to an ArrayList of String[] - something seems to go wrong, as all arrays seem to have the same values in their respective index positions and I don't have a clue why that is.
Here's my code, I'd appreciate if someone can spot the error:
public ArrayList<String[]> getDataDictionary(ArrayList<String> dbInfo, String table) throws SQLException {
ArrayList<String[]> result = new ArrayList<String[]>();
String[] resultTemp = new String[2];
... connection variables (URL, User, Pass)
... get connection, etc.
try {
Statement statement = con.createStatement();
ResultSet rs = statement.executeQuery("SELECT COLUMN_NAME,DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '" + table + "'");
while (rs.next()) {
resultTemp[0] = rs.getString("COLUMN_NAME");
resultTemp[1] = rs.getString("DATA_TYPE");
// database values
System.out.println(rs.getString("COLUMN_NAME"));
System.out.println(rs.getString("DATA_TYPE"));
// array values
System.out.println(resultTemp[0]);
System.out.println(resultTemp[1]);
//The above sout's return the proper values for each pass of the loop
//This is what feels the strangest to me. The values are correct here, but when queried later they are wrong
result.add(resultTemp);
}
String[] test = new String[2];
// sout's return wrong values now, i.e. the value returned is always the same for all arrays queried in the ArrayList
//I don't understand how that can be, because the correct values were added to the ArrayList a few lines above and now they are wrong with no changes made
test = result.get(0);
System.out.println(test[0]);
System.out.println(test[1]);
test = result.get(1);
System.out.println(test[0]);
System.out.println(test[1]);
test = result.get(2);
System.out.println(test[0]);
System.out.println(test[1]);
rs.close();
statement.close();
con.close();
return result;
} catch(Exception e)
{
e.printStackTrace();
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Error!");
alert.setHeaderText("Invalid SQL!");
alert.setContentText("Please verify the information you provided!");
alert.showAndWait();
return null;
}
You should create the array instance inside the loop.
while (rs.next()) {
String[] resultTemp = new String[2];
resultTemp[0] = rs.getString("COLUMN_NAME");
resultTemp[1] = rs.getString("DATA_TYPE");
....
Failing to do so causes the same array to be added to result multiple times.
You store a reference to the same object in every loop.
You have to create in every lopp a new Array:
while (rs.next()) {
resultTemp = new String[2];
resultTemp[0] = rs.getString("COLUMN_NAME");
resultTemp[1] = rs.getString("DATA_TYPE");
// database values
System.out.println(rs.getString("COLUMN_NAME"));
System.out.println(rs.getString("DATA_TYPE"));
// array values
System.out.println(resultTemp[0]);
System.out.println(resultTemp[1]);
//The above sout's return the proper values for each pass of the loop
//This is what feels the strangest to me. The values are correct here, but when queried later they are wrong
result.add(resultTemp);
}
You are overriding the same array , String[] resultTemp = new String[2];
while (rs.next()) {
String[] resultTemp = new String[2];
resultTemp[0] = rs.getString("COLUMN_NAME");
resultTemp[1] = rs.getString("DATA_TYPE");
Intialise it inside the while loop. So that when you add
result.add(resultTemp);
result will hold the list of resultTemp[] objects inside it .

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"));
}

Newbie pointer rs.next

I would like to see only that products user is looking for them, but when second if is executed it will push(pointer or whatever is there) to next ID(id I have as unique so it will push to nowhere) and result is null. I hope you understand my problem :).
if (stmt.execute(
"SELECT * FROM products where ID=" + removeName)) {
rs = stmt.getResultSet();
if (!rs.next()) {
m = "ID not found.";
return m;
}
In your case, you can go for PreparedStatement for avoiding SQL-Injection problem.
PreparedStatement prodsQuery= con.prepareStatement("SELECT * FROM products where ID=?");
prodsQuery.setInt(1,removeName);
ResultSet rs = prodsQuery.executeQuery();
if(!rs.next())
{
m = "ID not found.";
return m;
}
The problem is that you're reading the first result in order to know if there's at least one result, then trying to consume the next results and missing the first one (adapted from your question description). I gave an explanation of how this works here.
A possible solution for this problem would be assuming the query executed with no problems and you have your results, then retrieve the data (or List of data) and as a last step verify if the data is not null or the List of data is not empty.
Code adapted from Naveen's answer to show the proposed solution
PreparedStatement prodsQuery =
con.prepareStatement("SELECT * FROM products where ID=?");
prodsQuery.setInt(1,removeName);
ResultSet rs = prodsQuery.executeQuery();
Assuming there's only one result to get:
//also assuming you will set the results in a Data class (yes, this can be replaced)
Data data = null;
if (rs.next()) {
//logic to retrieve data...
data = new Data();
data.setSomething(rs.get(1));
//more and more code to fill the data...
//because it looks that you need it as String (wonder why you return a String as well)
return data.toString();
}
//note: I use an else statement to check if indeed there were no results at all
//else statement added using a line separator for code explanation purposes
else {
m = "ID not found.";
return m;
}
Assuming there is a list of results to get:
//also assuming you will set the results in a Data class (yes, this can be replaced)
List<Data> dataList = new ArrayList<Data>();
while (rs.next()) {
//logic to retrieve data...
Data data = new Data();
data.setSomething(rs.get(1));
//more and more code to fill the data...
//because it looks that you need it as String (wonder why you return a String as well)
dataList.add(data);
}
//in this case, there's no validation in order to know if there's any result
//the validation must be in the client of this class and method checking if
//the result list is empty using if(!List#isEmpty) { some logic... }
return dataList;
First thing, your approach is vulnerable to SQL Injection. Please go for PreparedStatement.
Look at this simple example for using PreparedStatement
And you should do like this :
ResultSet rs = stmt.executeQuery("SELECT * FROM products where ID=" + removeName);
if (!rs.next()) {
m = "ID not found.";
return m;
}

Categories

Resources