java.sql.SQLException: No value specified for parameter 5. update database - java

I have this error code in the dialog box: java.sql.SQLException: No value specified for parameter 5 when i try to update my JTable/JTextFields into my SQL database.
I have checked similar questions on the site, but non seem to have the solution to my problem. I have checked the database, i have checked my connection code, the update code and can't find where this extra parameter making the problem should be? Please help a new beginner!
So now i understand that the problem is at WHERE id=? as i suspected, but my id only exist as a row count/main key in my SQL DB, so it is going to be different depending on which row you choose/click on, so i can't set a specific value beforehand at the pst.setInt(5, ? ). What to insert instead then - so i dont lose the automatic row count on my clients list in the JTable?
//This method contains all codes for database connection.
private void upDateDB() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/num klienter", "root", "");
PreparedStatement pst = con.prepareStatement("SELECT * FROM klient");
ResultSet rs =pst.executeQuery();
ResultSetMetaData StData = rs.getMetaData();
q = StData.getColumnCount();
DefaultTableModel RecordTable = (DefaultTableModel)table.getModel();
RecordTable.setRowCount(0);
while(rs.next()){
Vector<String> columnData = new Vector<String>();
for (i = 1; i <= q; i++) {
columnData.add(rs.getString("id"));
columnData.add(rs.getString("Name"));
columnData.add(rs.getString("Birthday"));
columnData.add(rs.getString("Description"));
columnData.add(rs.getString("Other"));
}
RecordTable.addRow(columnData);
}} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex);
}}
updateButton.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent arg0) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost/num klienter", "root", "");
PreparedStatement pst = con.prepareStatement("UPDATE klient SET Name=?,Birthday=?,Description=?,Other=? WHERE id=?");
table.getSelectedRow();
pst.setString(1, nameTxt.getText());
pst.setString(2, dayTxt.getText()+"-" + monthTxt.getText()+"-" + yearTxt.getText());
pst.setString(3, descriptionTxt.getText());
pst.setString(4, otherTxt.getText());
pst.executeUpdate();
JOptionPane.showMessageDialog(null, "Updated in database");
upDateDB();
}catch (Exception ex){
JOptionPane.showMessageDialog(null, ex);
}

Class.forName("com.mysql.cj.jdbc.Driver");
this can just be removed. It hasn't been neccessary for 20 years.
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/num klienter", "root", "");
This is a memory leak; the connection is opened, and will remain open forever; your SQL server won't allow more than a handful of open connections, so pretty soon your MySQL server will be unreachable by any service (including this java code) until you kill the java app which closes the connections. Use try with resources for all resources you create.
PreparedStatement pst = con.prepareStatement("SELECT * FROM klient");
This is also a resource and needs try-with-resources.
ResultSet rs =pst.executeQuery();
You guessed it. Try-with-resources a third time. If you find the code becoming unwieldy, JDBC is very low level and not all that great for 'end user' coding. Use a nice abstraction like JDBI or JOOQ.
columnData.add(rs.getString("Fødselsdag"));
non-ASCII in your column names? That's never going to go well. I strongly suggest you don't do it this way.
q = StData.getColumnCount();
for (i = 1; i <= q; i++) {
This is bizarre. q holds the column count - that's the number of columns in your query. And then you hardcode the 5 column names, so q is always 5. Then, you add all 5 values (id, Navn, Fødselsdag, etc), and then do that 5 times, for a total of 25 runs, and your data repeated 5 times. It is not clear what you are trying to accomplish by asking for the known information (get the column count from the metadata, which you already know).
PreparedStatement pst = con.prepareStatement("UPDATE klient SET Navn=?,Fødselsdag=?,Beskrivelse=?,Andet=? WHERE id=?");
I count 5 ?, but only 4 pst.setString statements. You forgot pst.setInt(5, theValue).
The update code gets all the same caveats about try-with-resources.
pst.setString(2, dayTxt.getText()+"-" + monthTxt.getText()+"-" + yearTxt.getText());
Not how you do date stuff with DBs. There is a pst.setDate, but optimally you should use pst.setObject, passing an instance of java.time.LocalDate. Whether MySQL actually supports that - not sure, you'd have to check.

The solution for my problem was to insert the 5th pst. statement for the id=? like this:
pst.setInt(5,table.getRowCount());

Related

H2 Database result set is readonly

I'm getting the SQLNonTransientException error when trying to update one of my rows in a H2 database.
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
//read data from database
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM BCSTASKS_SERVICE");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
if(rs.getString("Status").equals("Neu") && rs.getBoolean("wasShown") == false) {
rs.updateBoolean("WASSHOWN", true);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
The error message already suggests that I should use conn.createStatement and set the ResultSet to CONCUR_UPDATABLE. The error occurs at the line with rs.updateBoolean(...);
Error Message:
The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE). [90140-210]
The problem is I don't know where and how I should use this method. In the same function or at the start of the program?
Most DB code I see doesn't attempt to use the fact that resultsets are updatable, and will instead fire off an additional UPDATE query, which works fine.
However, sure, H2 supports updateable resultsets too. However, some of the features that ResultSets have actually have quite a cost; the DB engine needs to do a boatload of additional bookkeeping to enable such features which have a performance cost. Lots of database queries are extremely performance sensitive, so by default you do not get the bookkeeping and therefore these features do not work. You need to enable them explicitly, that's what the error is telling you.
You're currently calling the 'wrong' preparedStatement method. You want the more extended one, where you pick and choose which additional bookkeeping you want H2 to do for you, in order to enable these things. You want this one.
conn.prepareStatement(
"SELECT * FROM BCSTASKS_SERVICE",
ResultSet.TYPE_SCROLL_INSENSITIVE, // [edited]
ResultSet.CONCUR_UPDATABLE);
That CONCUR_UPDATABLE thing is just a flag you pass to say: Please do the bookkeeping so that I can call .update.
[edited] This used to read 0 before, but as #MarkRotteveel pointed out, that's not valid according to the documentation.
You have to put update query for update data in database but you are going with select query that is the problem.
Select query is used if you have to fetch data from database.
Update query is used for update data in database where data already stored in database but you just overwrite data.
Here down is modified code:
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
PreparedStatement stmt = conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET wasShown = ? WHERE status = ? AND wasShown = ?");
stmt.setBoolean(1, true);
stmt.setString(2, "Neu");
stmt.setBoolean(3, false);
stmt.executeUpdate();
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
You need to create a separate query/prepareStatement for an update. In your case as far as I can see you need only one update query:
conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET WASSHOWN=true where
Status = 'Neu' and wasShown = false "

How should i make sure if the data already exists in the database

My code is having some error, and when I'm writing a roll number which is not present in the database and pressing the delete button then also a message pops up saying "record deleted successfully".
Actually I wanted to create a project of students report and by connecting java and MySQL. So I wrote code for the delete button, in which if the roll no of a student is written and pressed delete it will delete the record of that particular student.
so hope u understood my problem and looking forward for an accurate answer.
Class.forName("java.sql.DriverManager");
Connection con=DriverManager.getConnection("jdbc:mysql://localhost/stud","root","");
Statement stmt=con.createStatement();
String query="delete from info where rollno="+rn.getText();
int d = stmt.executeUpdate(query);
JOptionPane.showMessageDialog(null,"record deleted successfully!!!");
rn.setText("");
First of all, use PreparedStatement in which you fill in the parameters instead of composing a SQL-string.
May avoid very nasty errors (How does the SQL injection from the "Bobby Tables" XKCD comic work?). So
PreparedStatement stmt = con.prepareStatement("DELETE FROM info WHERE rollno=?");
stmt.setLong(1, Long.parseLong(rn.getText()));
int d = stmt.executeUpdate();
As far as your question is concerned:
The method executeUpdate returns the number of rows affected.
If it equals 0, no rows were deleted.
if (d == 0)
{
JOptionPane.showMessageDialog(null,"This record does not exist");
// Return or thrown an exception or whatever to interrupt the operation
}
else
JOptionPane.showMessageDialog(null,"record deleted successfully!!!");
showMessageDialog should get executed only if the value of d variable is positive i.e. some records got deleted from database. e.g.
Class.forName("java.sql.DriverManager");
Connection con=DriverManager.getConnection("jdbc:mysql://localhost/stud","root","");
Statement stmt=con.createStatement();
String query="delete from info where rollno="+rn.getText();
int d = stmt.executeUpdate(query);
if(d>0){
JOptionPane.showMessageDialog(null,"record deleted successfully!!!");
}
rn.setText("");
Check if result of executeUpdate is > 0. If yes, your entry was deleted.
Enter in rn: 1 or 1=1 and enjoy. Using PreparedStatements will prevent this evil SQL injection. Also it takes care of apostrophes around SQL strings and escaping apostrophe and other chars.
Connection con=DriverManager.getConnection("jdbc:mysql://localhost/stud","root","");
String query="delete from info where rollno=?";
try (PreparedStatement stmt = con.prepareStatement(query)) {
stmt.setLong(1, Integer.parseLong(rn.getText()));
int d = stmt.executeUpdate();
if (d != 0) {
JOptionPane.showMessageDialog(null, "Record deleted successfully.",
JOptionPane.INFORMATION_MESSAGE);
}
}
This try-with-resources will ensure that stmt is always closed

How to insert two strings into my Access database from Java using UCanAccess?

I am trying to add two strings on two separate columns columns of my database using Java but I'm not sure what I am doing wrong. The code I am using
try{
Connection conn = DriverManager.getConnection(
"jdbc:ucanaccess://C:/Users/nevik/Desktop/databaseJava/Employee.accdb");
Statement st = conn.createStatement();
String sql = "Select * from Table2";
ResultSet rs = st.executeQuery(sql);
rs.updateString("user", user);
rs.updateString("pass", pass);
rs.updateRow();
}
catch(SQLException ex){
System.err.println("Error: "+ ex);
}
The first column on my database is user and the next one is pass. I am using UCanAccess in order to access my database.
This is how you normally update a row in java:
String query = "update Table2 set user = ?, pass= ?";
PreparedStatement preparedStmt = conn.prepareStatement(query);
preparedStmt.setInt (1, user);
preparedStmt.setString(2, pass);
// execute the java preparedstatement
preparedStmt.executeUpdate();
First of, you've not updated the position of the current cursor in the ResultSet, which means that it's pointing to nothing...
You could use...
if (rs.next()) {
rs.updateString("user", user);
rs.updateString("pass", pass);
rs.updateRow();
}
But this assumes two things...
You have a database that supports updating values in the ResultSet and
You want to update the existing values.
To insert a value into the database, you should be using the INSERT command, for example...
try(Connection conn = DriverManager.getConnection(
"jdbc:ucanaccess://C:/Users/nevik/Desktop/databaseJava/Employee.accdb")) {
try (PreparedStatement stmt = conn.prepareStatement("INSERT into Table2 (user, pass) VALUES (?, ?)") {
stmt.setString(1, user);
stmt.setString(2, pass);
int rowsUpdated = stmt.executeUpdate();
}
}
catch(SQLException ex){
System.err.println("Error: "+ ex);
}
You might like to take some time to go over a basic SQL tutorial and the JDBC(TM) Database Access trail
As a side note...
You should not be storing passwords in Strings, you should keep them in char arrays and
You should not be storing passwords in the database without encrypting them in some way
#guevarak12
About the original question (how to use updatable ResultSet):
your code is wrong, you have to move the cursor in the right position.
In particular, if you are inserting a new row you have to call rs.moveToInsertRow(); before rs.updateString("user", user).
If you are updating an existent row, you have to move the cursor calling rs.next() and so reach the row to update.
Also you have to create the Statement in a different way:
Statement st =conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
See junit examples in the UCanAccess source distribution, class net.ucanaccess.test.CrudTest.
All other comments seem to be correct.

Loading entries from MySQL into Java table based on search field

I have a MySQL table with entries already in it and I have it connected to my Java program so it displays the table values whenever the program is run. I'm basically trying to implement a search field where the user can type any attribute's value and all the entries that match that value will be loaded into the table. Then the user will be able to select the right entry that matches and they can edit, or update that entry's information. This would be useful for me particularly when you have entries that have the same value, for instance first name, last name, or zip code.
try {
String sql = "SELECT * FROM donors WHERE donor_id = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, txtSearch1.getText());
rs = ps.executeQuery();
tblDonors.setModel(DbUtils.resultSetToTableModel(rs));
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e);
}
try {
String sql = "SELECT * FROM donors WHERE first_name = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, txtSearch1.getText());
rs = ps.executeQuery();
tblDonors.setModel(DbUtils.resultSetToTableModel(rs));
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e);
}
The search field only searches for the second query, but not the first, so I can type a name and the matching names will load into the table, but when I try to input an id number, nothing happens. I'm fairly new to this, but I think it has something to do with my resultset object? Not exactly sure though. Any help would be great.
What happens here is that the second result overwrites the first. I think the easiest solution is to use or in the where clause, like this:
String sql = "SELECT * FROM donors WHERE (donor_id = ?) or (first_name = ?)";
ps.setString(1, txtSearch1.getText());
// but of course there are 2 ?'s now, we have to give the value to the second one
// as well
ps.setString(2, txtSearch1.getText());
Due to the way placeholders work in JDBC you'll have to provide a value for each ?.

Why SQL select takes more CPU time in java?

I have a java web application that selects one column from table (with 6 million rows) and it takes a lot of CPU time. This select (SELECT id FROM mytable WHERE filename = 'unique_filename') takes significantly less time when executed in query browser.
What can cause this?
Where should I start to look for bottlenecks?
Database is MSSQL 2005 Standard
Java container is Tomcat 5.5 (with sqljdbc 1.2)
More details:
1.Java code
ResultSet rs = null;
PreparedStatement stmt = null;
Connection conn = null;
Integer myId=null;
String myVeryUniqueFileName = strFromSomeWhere;
try
{
conn = Database.getConnection();
stmt = conn.prepareStatement("SELECT id FROM mytable WHERE filename = ?");
stmt.setString(1, myVeryUniqueFileName);
rs = stmt.executeQuery();
if (rs.next())
{
myId= new Integer(rs.getInt(1));
} }
if (rs.next())
{
throw new DBException("Duplicate myId: " + myId);
}
return myId;
} catch (Exception e) {
// handle this
}
The Database object uses DriverManager to receive connection object.
2.SQL table has about 30 columns.
CREATE TABLE [dbo].[calls](
[id] [int] NOT NULL,
...
[filename] [varchar](50) NOT NULL,
...
CONSTRAINT [PK_xxxxxxxxxxxx] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY],
CONSTRAINT [UQ_xxxxxxxxxxxx] UNIQUE NONCLUSTERED
(
[filename] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
filename column is unique so result set from is allways 1 or null.
With help of way smarter developer I was able to solve this problem. Turns out I was misusing PreparedStatement (aricle).
Based on this I changed java code to:
ResultSet rs = null;
Statement stmt = null;
Connection conn = null;
Integer myId=null;
String myVeryUniqueFileName = strFromSomeWhere;
try
{
conn = Database.getConnection();
stmt = conn.createStatement()
//
rs = stmt.executeQuery("SELECT id FROM mytable WHERE filename = '"
+ myVeryUniqueFileName + "'");
if (rs.next())
{
myId= new Integer(rs.getInt(1));
}
if (rs.next())
{
throw new DBException("Duplicate myId: " + myId);
}
return myId;
} catch (Exception e) {
// handle this
}
After this dababase load fell from average 70% to 13%
Can you post your java code where you're executing this query and retrieving the results?
Possible factors causing Java code to appear to take significantly longer are:
Your query returns a large number of records and you're trying to retrieve them all in Java whereas query browser would only show the first 100 (whatever that number may be) and load others on demand.
You're comparing different times, for example "query took X ms" shown by your query browser with time it takes Java from obtaining the connection till closing it.
Your objects (holding the results) may be expensive to create or they may be doing some processing behind the scenes as they're populated.
I can't speak to MSSQL 2005 specifically, but there can be a difference in execution plan between a prepared statement where you're using bind variables and the equivalent statements where values are embedded.
To test this theory, drop the bind parameter, and instead concatenate the SQL query in Java with the actual filename (in quotes). This way you're comparing apples to apples.
Also, it would be useful with an indication of the difference in CPU time you're experiencing. Is it several orders of magnitude or less than 100%.
The symptoms you describe are most often caused by an incorrectly cached query plan.
Rebuild your indexes or update your statistics.
You are using a statement probably and not a prepared statement. A statement does not get precompiled and cached so the query optimizer has to do the work everytime. If you use a prepared statement it will try and find the best way to execute your query and it will store that. The next time you use it it won't bother to try and work out a good way to get your results it will just the execution plan it already has.

Categories