So I have a database that is made like this
{MATERIAL NAME;QUANTITY}
I also have a JTable in Java that uploads the info from the database (SQLite). As I edit a cell in the table, it automatically updates the database in this way:
//table listener
public void tableChanged(TableModelEvent e) {
//gets row and column if the table is edited
int row = e.getFirstRow();
int column = e.getColumn();
//change in sqlite
if (column == 1) {
int value = Integer.parseInt(table.getModel().getValueAt(row, column).toString());
String materialId = table.getModel().getValueAt(row, column-1).toString();
try (Connection c = DriverManager.getConnection("jdbc:sqlite:database.db"); Statement statement = c.createStatement()) {
String sql = "UPDATE MATERIALS set QUANTITY = " + value + " where MATERIAL='" + materialId +"';";
statement.executeUpdate(sql);
statement.close();
c.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
else if (column == 0) {
int value = Integer.parseInt(table.getModel().getValueAt(row, column+1).toString());
String materialId = table.getModel().getValueAt(row, column).toString();
try (Connection c = DriverManager.getConnection("jdbc:sqlite:database.db"); Statement statement = c.createStatement()) {
String sql = "UPDATE MATERIALS set MATERIAL = " + materialId + " where MATERIAL='" + materialId +"';";
statement.executeUpdate(sql);
statement.close();
c.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
It's okay when the quantity is updated. However, once the material name is updated, I don't know how I am supposed to update it in the database.
String sql = "UPDATE MATERIALS set MATERIAL = " + materialId + " where MATERIAL='" + materialId +"';";
Maybe there is a way to save a previous value of that value? I can not use the quantity of materials as they might be repeated.
I would like to avoid adding IDs to the objects if possible.
This statement:
String sql = "UPDATE MATERIALS set MATERIAL = " + materialId + " where MATERIAL='" + materialId +"';"
Has two (three) problems.
The 'old' value and the 'new' value are the same. You have a logical problem.
WHERE part is concatenated like for string type, but SET part is not
concatenation is evil. The Correct way to do this is
String sql = "UPDATE MATERIALS set MATERIAL = ? where MATERIAL= ?";
PreparedStatement prepstmt = conn.prepareStatement(sql);
prepstmt .setString(1, newMatewrial);
prepstmt .setString(2, oldMaterial);
prepstmt .executeUpdate();
Logical problem must be resolved in your conception.
It Seems the main problem is in basically not using the ID . Every row in relational database should have stable primary key (ID like You say). Name isn't primary key, because can be changed.
WHERE part should use ID and not name.
EDIT: in my opinion good JTable model is one way to solve your problem.
Maybe automagic default table model is too poor. Class implementing Row should have one more column (ID) which can(or not) be invisible.
My English isn't too fluent, cannot teach relational database and Swing in SO post.
The second, small snippet of code doesn't show your application conception.
Read in google about custom JTable model.
Very basic example:
http://www.java2s.com/Code/Java/Swing-JFC/TablewithacustomTableModel.htm
Okay so it seems that I found the solution to the problem
JTable: Detect cell data change
However, this requires implementing another class into the project and using it. The answer for the SQL statement still stays the same.
Related
I am making a program using Eclipse that allows the user to update the volume of chemicals everytime they’re restocked/used, which requires them to enter the ID of the chemical and the amount they would like to add/subtract. A query is then performed to search for the chemical's ID in the database, and its volume is updated accordingly.
However, I’m having difficulties getting the volume to update. I tried adapting MySQL’s UPDATE statement from this website to SET volume = volume + amount added, WHERE chemical ID = ID entered by the user; however, there appears to be some syntax errors in my code, more specifically at the UPDATE - SET - WHERE line:
public void IDEnter() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:8889/StockControlSystem","root","root");
Statement stmt = con.createStatement();
String sql = "Select * from Chemicals where `Chemical ID` ='" + txtChemical_ID.getText()+"'";
ResultSet rs = stmt.executeQuery(sql);
if(rs.next()) {
stmt.executeUpdate("UPDATE Chemicals" + "SET `Volume` = rs.getInt(Volume) + Integer.parseInt(AmountAdded.getText()) WHERE `Chemical ID` in (txtChemical_ID.getText())");
}
else {
JOptionPane.showMessageDialog(null, "Invalid chemical ID");
txtChemical_ID.setText(null);
}
} catch(Exception exc) {
exc.printStackTrace();
}
}
Since I'm still new to MySQL, can someone help me correct this? Thank you so much for your help!
Your whole query is badly formatted. Change your code to this:
stmt.executeUpdate("UPDATE Chemicals SET Volume = " +
rs.getInt(Volume) + Integer.parseInt(AmountAdded.getText())
+ " WHERE Chemical_ID in (" + txtChemical_ID.getText() + ")");
You cannot use ' single quotes when defining Column names in queries. Single quotes are used for string values!
Still, this would not be the best way to do this. use PreparedStatement!
This way:
String updateString = "UPDATE Chemicals SET Volume = ? WHERE Chemical_ID in (?)"; // Creation of the prepared statement, the ? are used as placeholders for the values
PreparedStatement preparedStatement = con.prepareStatement(updateString);
preparedStatement.setInt(1, rs.getInt(Volume) + Integer.parseInt(AmountAdded.getText())); // Setting the first value
preparedStatement.setString(2, txtChemical_ID.getText()); // Setting the second. I am supposing that this txtChemical_ID textField has values seperated by commas, else this will not work!
preparedStatement.executeUpdate();
If you need to read more for PreparedStatement there are a lot of great resources out there. They also protect against SQL injections.
I think your problem might be with the "rs.getInt(Volume)"
Yours:
"UPDATE Chemicals" + "SET `Volume` = rs.getInt(Volume)
+ Integer.parseInt(AmountAdded.getText())
WHERE `Chemical ID` in (txtChemical_ID.getText())"
Can you try this:
"UPDATE Chemicals" + "SET `Volume` = " +
Integer.parseInt(AmountAdded.getText()) + "
WHERE `Chemical ID` in (" + (txtChemical_ID.getText()) +")"
So I am working on AS400, DB2 System.
I wrote a method which provides me the Primary Keys of every physical table. But on some tables the primary keys are only set on the logical table. There my method does not work.
#Override
public ArrayList<Field> getPKS(String lib) {
ArrayList<Field> pkList = new ArrayList<>();
try (Connection connection = DriverManager.getConnection("jdbc:as400://" + ConnectionData.SYSTEM + ";naming=system;libraries=*" + lib + ";",
ConnectionData.USER, ConnectionData.PASSWORD);
ResultSet rs = connection.getMetaData().getPrimaryKeys(null, connection.getSchema(), "LSAVSLA")){
while (rs.next()) {
pkList.add(new Field(rs.getString("COLUMN_NAME")));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return pkList;
}
For a pysical table it's working, but for a logical Table it is not.
Do you have any idea how to get the primary keys from the logical table.
Logical files do not contain data. They contain a description of records that are found in one or more physical files. A logical file is a view or representation of one or more physical files
says the manual. Similarly to a view in a conventional RDBMS, you cannot define a primary key for a logical file.
So basically what you have are physical files (or SQL tables) defined without a primary key and logical files or (SQL indexes) defined with a unique key.
On the IBM i, a logical file can act as either an SQL index, and SQL View or both at the same time. As Mustaccio mentions, there isn't any actual data in the object.
You best bet, might be to query the SYSTABLEINDEXS catalog view looking for primary key or unique indexes over a given table.
You could also take a look at the getIndexes() method.
I found a solution by selecting the "DBKFLD" field from "QSYS.QADBKATR"
The SQL Query: SELECT DBKFLD FROM QSYS.QADBKATR WHERE DBKLIB = "Your lib" AND DBKFIL = "Your table"
The Java Code:
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet resultSetQuery = statement.executeQuery("select DBKFLD from QSYS.QADBKATR where DBKLIB = '" + lib + "' and DBKFIL = '" + tablename + "'")) {
ResultSetMetaData metadata = resultSetQuery.getMetaData();
int columnCount = metadata.getColumnCount();
while (resultSetQuery.next()) {
for (int i = 1; i <= columnCount; i++) {
String pk = resultSetQuery.getString(i);
pk = pk.replaceAll("\\s+", "");
pkList.add(new Feld(pk));
}
}
return pkList;
I am thinking about the design of the method that will enable the user to potentially pass a list of integers that indicate the columns that the user wishes to retrieve from the database.
I do not want to hardcode multiple methods that esentially do the same thing, i.e. show the user different columns but from the same table.
here is the code from Oracle tutorials on retrieving the values using JDBC:
public static void viewTable(Connection con, String dbName)
throws SQLException {
Statement stmt = null;
String query =
"select COF_NAME, SUP_ID, PRICE, " +
"SALES, TOTAL " +
"from " + dbName + ".COFFEES";
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String coffeeName = rs.getString("COF_NAME");
int supplierID = rs.getInt("SUP_ID");
float price = rs.getFloat("PRICE");
int sales = rs.getInt("SALES");
int total = rs.getInt("TOTAL");
System.out.println(coffeeName + "\t" + supplierID +
"\t" + price + "\t" + sales +
"\t" + total);
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
} finally {
if (stmt != null) { stmt.close(); }
}
}
So the query is not a problem, the column names can be concatenated, depending on which columns the user wants to see. The issue is in the try block. How does one .get the correct format from the result set? Or should I simply use the String for every column? Or should I hardcode all the table columns (rs.get depending on what data type the column is) and then in println return only the columns that the user wishes to see (actually how would I do that)? Well, I guess you understand my issue.
You can retrieve all the data from the particular table and use it to populate a collection of the appropriate object. And then based on the user's choice, you could just print out the appropriate columns.
Assuming you know how to create the collection of the appropriate object, I will explain how you can do the next step.
You can display a message to the user asking him to enter the columns he wishes to see. Like, Enter 1 to see the coffee name, 2 to see the supplier id, 3 to see the price etc. and 0 to view the data.
So you basically keep reading the int's until the user enters a 0. Once he enters a zero, display the requested values.
I want to create an Access database connection using UCanAccess under Java 8. Here is my code:
String employeeName = endrollNameFields.getText();
String employeeAddress = endrollAddressFields.getText();
try
{
//------------CREATE CONNECTION TO DATA BASE--------------/
String DBPAD = "sourceFolder\\employeeTable2.mdb";
String DB = "jdbc:ucanaccess://" + DBPAD;
con = DriverManager.getConnection(DB, "", "");
st = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql = "select * from employeeTable2";
rs = st.executeQuery(sql);
rs.moveToInsertRow();
rs.updateString("Name", employeeName);
rs.updateString("Address", employeeAddress);
rs.insertRow();
st.close();
st = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql1 = "select * from employeeTable2";
rs = st.executeQuery(sql1);
JOptionPane.showMessageDialog(null, "<html>" + "<font color=\"#008000\">" + "<html><span style='font-size:1.5em'>Employee Successfuly Inserted to Data Base");
}
catch(Exception e1)
{
JOptionPane.showMessageDialog(null, e1);
}
I made entries to my JTextField but when i click the button containing codes above this error message appears:
net.ucanaccess.jdbc.Ucanaccess SQLException:invalid cursor state: all columns must be set before insert
What is missing or lacking in my code?
UCanAccess uses HSQLDB as a "backing database", and HSQLDB requires that all columns in the insert row be given an explicit value before calling .insertRow(), including using rs.updateNull("ColumnName") to explicitly specify a NULL value. That's just the way it works.
This was discussed recently on SourceForge here. The requirement may be lifted in a future version of UCanAccess.
UPDATE
UCanAccess version 3.x, released in August 2015, has indeed removed the above requirement. Any columns not explicitly set in the insert row will contain the default value for the column (or NULL if the column is nullable and no default value is specified in the table definition).
I have to add two queries to a table model so that it shows up on the table. It is a program doing prediction on soccer (EPL to be precise) and I need to display ALL results for the team when they are playing both home, and away. the first query is to get all the games where they play home, the second query is when they play away. Here is the code:
public void showResultsTotalTeam(){
deleteAllRows(dTableModel); // deleta all rows in the table
try {
conn = DriverManager.getConnection(connection.conn_url, connection.conn_user, connection.conn_pass);// connect to database server
Statement sqlState = conn.createStatement();// create statement for sql
String selectStuff = "SELECT games_team1, games_team2, games_winner, games_draw, games_team1_score, games_team2_score, games_month, games_day FROM games WHERE games_team1 = '" + cbxTeam1.getSelectedItem() + "'";// ststement for MySQL
rows = sqlState.executeQuery(selectStuff); // execute statement
String selectStuff2 = "SELECT games_team1, games_team2, games_winner, games_draw, games_team1_score, games_team2_score, games_month, games_day FROM games WHERE games_team2 = '" + cbxTeam1.getSelectedItem() + "'";// ststement for MySQL
rows2 = sqlState.executeQuery(selectStuff); // execute statement
Object[] tempRow;// create object array to store queried results
Object[] tempRow2;
while(rows.next()){ // while there are still values to be seen to
tempRow = new Object[]{rows.getString(1), rows.getString(2), rows.getString(3), rows.getString(4), rows.getString(5), rows.getString(6), rows.getString(7), rows.getString(8)};// add data to array
tempRow2 = new Object[]{rows2.getString(1), rows2.getString(2), rows2.getString(3), rows2.getString(4), rows2.getString(5), rows2.getString(6), rows2.getString(7), rows2.getString(8)};
dTableModel.addRow(tempRow); // add array to table model
dTableModel.addRow(tempRow2);
}
} catch (SQLException ex) {
// TODO Auto-generated catch block
System.out.println(ex.getMessage());
}
}
Now this code does not work and nothing shows up at all.
Please help? Any advice would be great.
You haven't loaded thr driver class
Class.forName("Driver Class Name");
And you haven't closed anything. Connecton & ResultSet
Unless the number of rows == number of rows2 then you may encounter trouble when you are iterating through rows.next
Also suggest you iterate do you code in two distinct blocks i.e one for rows and one for rows2
Edit
This could also be done with one query
SELECT games_team1, games_team2, games_winner, games_draw, games_team1_score,
games_team2_score, games_month, games_day FROM games
WHERE games_team1 = '" + cbxTeam1.getSelectedItem() + "' "
or games_team2 = '" + cbxTeam1.getSelectedItem() + "' "