JDBC: Create Table with PreparedStatement, SQL Syntax Error or not? - java

I am encountering some difficulties trying to, in a dynamic way, create a new table using PreparedStatement. I am trying to add a unknown number of columns and their respective types and sizes. I keep getting SQL Syntax errors, but I suspect this may not be the case here.
Variables used:
con = Connection object
colNames = String[] containing column names ("person", "email" etc)
colTypes = String[] containing column types ("varchar", "int" etc)
colSizes = String[] containing column sizes ("100", "11", etc)
Should be pretty self-explanatory.
con.setAutoCommit(false);
String sql = "? ?(?),";
PreparedStatement ps = con.prepareStatement(sql);
ps.addBatch("create table " + tablename + "( ");
for (int i = 0; i < colNames.length; i++){
if (!(i == colNames.length-1)) {
ps.setString(1, colNames[i]);
ps.setString(2, colTypes[i]);
ps.setString(3, colSizes[i]);
} else {
String format = "%s %s(%s)";
String lastLine = String.format(format, colNames[i], colTypes[i], colSizes[i]);
ps.addBatch(lastLine);
}
}
ps.addBatch(");");
ps.executeBatch();
NOTE: Yes, this is homework. I don't want any dead giveaways, rather pointers as to in what way I am misusing some functions, which I suspect.
Best regards,
Krys

You need to give the full SQL statement to addBatch. It is not a tool to construct a dynamic SQL statement. It is a tool to improve performance when running multiple statements. You don't need it here.
You also don't need a PreparedStatement here, as you are not going to have bind variables (i.e. column data as opposed to column names) and are not going to run the same SQL repeatedly (but it does not hurt, either). setString and friends do not work for column or table names, just for data.
A StringBuilder is a good tool to construct a String with variable parts.

Related

SQL Sum Query works in PHPmyAdmin, but not in Java resutset

I have a table called "Transactions". It has 5 attributes: Date, Description, Amount, Clientname, Transaction_ID where Transaction_ID is the primary key. In the example Data, the Clientname, "John Smith" has two transactions where he spend 100.10 and 56.56 each. The SQL Query returns the expected result of 156.66 in PHPMyAdmin, but JDBC doesn't seem to recognize it in the ResultSet.
Here is my code:
public void calculate_client_spending() throws SQLException {
ConnectionClass Databaseloader = new ConnectionClass();
Databaseloader.getConnection();
String sql = "SELECT SUM(Amount) AS total FROM Transactions WHERE Clientname = 'John Smith';";
ResultSet rs = Databaseloader.executeSQLRequestCommand(sql);
// rs.next();
// System.out.println(sum);
ResultSetMetaData rsMetaData = rs.getMetaData();
int numberOfColumns = rsMetaData.getColumnCount();
System.out.println(numberOfColumns);
// get the column names; column indexes start from 1
for (int i = 1; i < numberOfColumns + 1; i++) {
String columnName = rsMetaData.getColumnName(i);
// Get the name of the column's table name
if ("total".equals(columnName)) {
System.out.println("Bingo!");
rs.last();
int count = rs.getRow();
rs.beforeFirst();
System.out.println(count);
while (rs.next()) {
Results_trasactions.setText("");
System.out.println("The total profits today are: " + rs.getString(1));
}
}
}
}
This Query returns null in this example, but if I did rs.getDouble(1), it would return 0. Any idea what the issue may be here? I am able to get similar SUM Query's to work, such as a SUM for all clients and the WHERE clause seems to work for my primary key, but this specific Query JDBC doesn't seem to like it even though the SQL is valid in PHPmyadmin which makes me want to believe that it is a Java issue and not a SQL issues. Any help would be greatly appreciated.
Since this is unfortunately way too long for a comment:
Not meaning to be mean, but maybe you shouldn't create a new account to answer your deleted question again (https://stackoverflow.com/questions/59570469) -> Google Cache - also, the Database Classes you're using give away your "real" account (How to retrieve the "total" variable representing a sum in a resultset) - so I'm voting to close this question yet again.
However, to be at least some hints:
ConnectionClass Databaseloader = new ConnectionClass();
Databaseloader.getConnection();
Databaseloader isn't any default JDBC class, but rather some (poorly) written static class, which looks like a weird wrapper to me. You can do it, but by any means you wouldn't do it statically. And by throwing the methods into Google, you find: almost nothing.
For mySQL you'd acquire a database connection like that:
Connection con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "sa", "sa")
and rather work with the connection object.
I'd recommend you to do the following:
Read how to connect your database in Java
Read how to create and execute a prepared statement
Read how to extract a result from a result set
Inform yourself about parameter binding (avoid SQL injections)
Profit!
All these topics are well covered on stackoverflow.

How to add rows to a table after the columns were altered? (SQLite, JAVA)

I am trying to add rows to a table. Initially, I had this code but it creates an error that there are 8 columns (additional columns were altered, initially I only had 2 columns) but I am only adding 2 values:
PreparedStatement addDate =
con.prepareStatement("insert into contributions values(?,?)");
addDate.setString(2, string);
addDate.execute();
}
And then I tried this:
public void addDateToContributionsTable(String string) throws ClassNotFoundException, SQLException {
if(con == null) {
// get connection
getConnection();
}
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("Select * from contributions");
ResultSetMetaData rsmd = rs.getMetaData();
int columnsNumber = rsmd.getColumnCount();
StringBuilder sb = new StringBuilder();
for(int i= 0; i < columnsNumber; i++){
sb.append("?");
sb.append(",");
}
System.out.println(sb.toString());
System.out.println("insert into contributions values('"+sb.toString()+"')");
PreparedStatement addDate = con
.prepareStatement("insert into contributions values('"+sb.toString()+"')");
addDate.setString(2, string);
addDate.execute();
}
But I am still having the same error:
[SQLITE_ERROR] SQL error or missing database (table contributions has 8 columns but 1 values were supplied)
Any help would be appreciated. :)
The values don't know where they should be inserted, so I suggest writing the column names for each value.
reparedStatement addDate =
con.prepareStatement("insert into contributions (COLUMN_NAMES) values (?,?)");
addDate.setString(2, string);
addDate.execute();
}
You added multiple bind parameters in the SQL, but you only ever bound one of them:
addDate.setString(2, string);
You need to call setXXX() per bind parameter.
If you are really sure you want to use the same value, you can instead use named parameters instead, then you won't have to call setXXX() multiple times. You can refer to this:
Named parameters in JDBC
Also, questionMarksList and StringBuilder sb are doing the same thing over two loops.
Edit
If your SQL has 2 question marks, you have 2 bind parameters, then you need to set two bind parameters.
e.g. For SQL with 3 bind parameters:
INSERT INTO MYTABLE VALUES(?, ?, ?)
You need to provide 3 values:
addDate.setString(1, "String1"); // Bind to first question mark
addDate.setString(2, "String2"); // etc.
addDate.setString(3, "String3");
Thanks to all that responded to my question. The problem was with the number of ? I had on my statement every time I increase or decrease the column numbers. The only problem with my statement is having this expression '' . When I changed it to "+questionMarks+" instead of '"+questionMarks+"', it worked. Does not matter how many setXXX() method I use as long as the number of ? are the same with the number of columns on the table, it will work. Having the code below, I did not encounter any errors anymore.
PreparedStatement addmembers = con
.prepareStatement("insert into membersdata values "+questionMarks+"");
addmembers.setString(2, name);
addmembers.setString(3, accounts);
addmembers.setString(4, email);
addmembers.execute();

SonarQube Report warning: 'A prepared statement is generated from a non constant string'

I am using below piece of code in a method:
final StringBuffer queryBuffer = new StringBuffer(100);
queryBuffer.append("update ").append(tblContainerItem).append(" ");
queryBuffer.append("set SENT_STATUS=").append(CaaConstants.STATUS_CI_SENT);
queryBuffer.append(", SENT_DATE='").append(today.toString()).append("' ");
final int len = containerItemIds.length;
for (int i = 0; i < len; i++) {
if (i == 0) {
queryBuffer.append("where CI_ID=");
} else {
queryBuffer.append(" or CI_ID=");
}
queryBuffer.append(containerItemIds[i]);
}
try {
conn = getConnection();
pstmt = conn.prepareStatement(queryBuffer.toString());
...
and it shows a warning at the this line: pstmt = conn.prepareStatement(queryBuffer.toString());
How can I avoid this warning? I know by using #SuppressWarnings but that is not the correct way of eliminating this error.
What you're constructing is dynamic sql, which is generally considered inherently dangerous. For example, you appear to have no limits on what table you'll be updating. Instead, you should be doing something like
String update = "update MY_TABLE set SENT_STATUS=?, SENT_DATE=? where CI_ID=?";
try {
conn = getConnection();
pstmt = conn.prepareStatement(update);
pstmt.setString(1, CaaConstants.STATUS_CI_SENT);
pstmt.setString(2, today.toString());
pstmt.setString(3, containerItemIds[i]);
//...
This does 2 things
makes sure you're really updating rows in the table you intend to update
protects you from malicious input by escaping the values being passed into the query
Note that if you've made the table name dynamic because you have several different tables with the same columns,
you should examine your DB structure for refactoring opportunities
you should write separate code for each one. Saving a few lines of code is not worth the risk of updating something you didn't intend.

Java INSERT to SQL Database with JDBC

Just looking for some small help here. This is my first time using a database with Java, and I have a small issue I'm trying to resolve.
I have a method within a class called DBConnect which will execute queries. I'd like to insert this List into my database.
List<String> data = new ArrayList();
data.add(name);
data.add(bank);
data.add(pin);
data.add(email);
data.add(pass);
data.add(phone);
data.add(paypal_email);
data.add(paypal_pass);
data.add(IPV4Assistant.getExternalIPAddress());
data.add(crypto);
data.add("1");
data.add(dob);
DBConnect.executeQuery();
I suppose I'd start creating the query string with
String insert = ("INSERT INTO Client_Data (card_number,card_pin,client_dob,crypto_currency_address,email,email_password,id,ip_address,name,paypal_email,paypal_password,phone_number) VALUES
The above fields being the columns I'm trying to insert into, and Client_Data being my table.
How do I go about formatting the fields in my list to query properly?
After Values I believe the format is ('data','data','data').
Could anybody experienced with JDBC please assist me?
Thank you.
I would use PreparedStatements to insert the values into your table.
/*
* Code
* I am assuming that you have a Connection object named conn.
* This is just a simple example
*/
try(
PreparedStatement ps = conn.prepareStatement(
"insert into yourTable(field1, field2, field3) values (?,?,?)"
) {
/*
* The question marks are placeholders for the values you will insert.
*/
ps.setString(1, "abc");
ps.setInt(2, 123);
ps.setDouble(3, 3.1416);
ps.execute(); // The insert is executed here
} catch(SQLException e) {
// Your exception handling code
}
If you need to insert values into your table using a loop, you may also execute the inserts as a batch:
/*
* Code
*/
try(
PreparedStatement ps = conn.prepareStatement(
"insert into yourTable(field1, field2, field3) values (?,?,?)"
) {
for(int i = 0; i < 10; i++) {
ps.setString(1, "abc");
ps.setInt(2, 123 * i);
ps.setDouble(3, 3.1416);
ps.addBatch(); // The insert is added to a batch, pending for execution
}
ps.executeBatch(); // All the inserts added to the batch are executed.
} catch(SQLException e) {
// Your exception handling code
}
Reference:
The Java Tutorials: JDBC - Using Prepared Statements (You may also want to read the full JDBC tutorial)
Java API reference for the PreparedStatement class
Basically, you should be trying to use PreparedStatement, there are a number of very good reasons for this, but in your case, it's the simplest way to bind the values from your List to the Statement
For example, you could start by defining the insert statement as a constant, this isn't required, but for the example, it made it easier...
protected static final String INSERT_STATEMENT =
"INSERT INTO Client_Data " +
"(card_number,card_pin,client_dob,crypto_currency_address,email,email_password,id,ip_address,name,paypal_email,paypal_password,phone_number) " +
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?)";
Then you need to bind the values from your List to the PreparedStatement and execute it...
List<String> data = new ArrayList();
data.add(name);
data.add(bank);
data.add(pin);
data.add(email);
data.add(pass);
data.add(phone);
data.add(paypal_email);
data.add(paypal_pass);
data.add(IPV4Assistant.getExternalIPAddress());
data.add(crypto);
data.add("1");
data.add(dob);
// Replace with your own connection management, just here for
// example reasons
try (Connection con = DriverManager.getConnection(url)) {
try (PreparedStatement stmt = con.prepareStatement(INSERT_STATEMENT)) {
for (int index = 0; index < data.size(); index++) {
stmt.setObject(index + 1, data.get(index));
int rows = stmt.executeUpdate();
// Check the value of rows if you want to know how
// many rows were affected by the change
}
}
} catch (SQLException exp) {
// Possibly throw this to the call instead...
exp.printStackTrace();
}
I assume, you'll be passing the List as an parameter to some method.
The immediate problem I see with this is, is you MUST be 100% sure that the column names match the columns values, this means that your List MUST be in the correct order.
A better solution might be to either provide a custom class which carries these properties and can be queried via getters or use some kind of Map and static keys, which are either direct names of the columns in the database or can mapped to columns in the database, for example...
public static final String CLIENT_NAME = "name";
//... Other column names/keys...
//...
Map<String, Object> clientData = new HashMap<String, Object>();
clientData.put(CLIENT_NAME, name);
//...
stmt.setObject(CLIENT_NAME, clientData.get(CLIENT_NAME));
You should also avoid inserting String into columns which have different data type requirements (such as Date, TimeStamp and/or numbers). Instead, you should be trying to use the correct JDBC mapping types where possible
Take a look at Using Prepared Statements for more details

Writing multiple where clauses in Java using Mysql

below is code is a code i wrote to get the value of 'monthly Depreciation' when i select the row on my j Table by either mouse-clicked or key-pressed. but it only selects the first value for 'monthly depreciation' when i click on the rows or key-press.the problem i know is coming from the where statement but can't seem to get around it.
if(evt.getKeyCode()==KeyEvent.VK_DOWN || evt.getKeyCode()==KeyEvent.VK_UP)
{
try{
int row =dep_report.getSelectedRow();
String Table_click=(dep_report.getModel().getValueAt(row, 0).toString());
String sql ="select Date_Acquired 'Date Acquired',Serial_Number 'Serial Number',"
+ " Description 'Description',Cost_Of_Acquisition 'Cost Of Acquisition',"
+ "Monthly_Depreciation 'Monthly Depreciation',Accumulated_Depreciation 'Accumulated Depreciation',Net_Book_Value 'Net Book Value'"
+ ",asset_update.Branch_Area 'Branch Area',Depts_name 'Department Name' ,User 'User',"
+ "Status 'Status' from items,asset_update where items.items_No = asset_update.items_No &&'"+Table_click+"'";
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
if(rs.next()){
String add1 = rs.getString("Monthly Depreciation");
MonthlyDep.setText(add1);
}
}
catch(Exception e)
{
JOptionPane.showMessageDialog(null, e);
}
I would really appreciate the help thank you.
In your sql
where items.items_No = asset_update.items_No &&'"+Table_click+"'";
&& wont work for sql and you might need
where items.items_No = asset_update.items_No and items.someThing= '"+Table_click+"'";
Please use Java naming conventions and give proper names to things Table_click is a horrible variable name.
But can you describe what is in your table model in the 1st column of the selected row?
You seem to append that to your query and if it does not contain a valid SQL part, this will not work well with your statement. In a where clause you usually check a column against a value. I doubt that your table model has this written there, more likely you just have the value in your table model at this position.
Also make sure to properly use prepared statements. Never put the values directly in the SQL string you create or you create the perfect entry point for SQL injection. Assign the values instead once you have created the statement with something like this: pst.setString(1, Table_click);

Categories