I keep getting an error on my statement when running the postmethod of my servlet. However, when I simply try to do it in the database directly it works just fine.
I'm using a tomcat 9 server; and postgres database. Java Openlogic 11;
The message I'm getting is:
ERROR: column "doe" does not exist
Position: 61
Which refers to the value I'm trying to insert.
What am I missing here? I'm still learning so any and all information is very welcome. Thanks in advance.
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String firstname = request.getParameter("firstname");
String lastname = request.getParameter("lastname");
String location = request.getParameter("location");
Statement stmt;
try {
stmt = con.createStatement();
int result = stmt.executeUpdate(
"INSERT INTO testinserts (lastname, firstname, city) VALUES ("+ lastname + "," + firstname + "," + location + ");");
PrintWriter writer = response.getWriter();
if(result > 0){
writer.println("<H1>" + firstname + " created " + "</H1>");
} else {
writer.println("<H1>" + "none created " + "</H1>");
}
} catch (SQLException ex) {
PrintWriter writer = response.getWriter();
writer.println(ex.getMessage());
StackTraceElement[] stackTrace = ex.getStackTrace();
writer.println("<HTML>");
for(StackTraceElement st : stackTrace){
writer.println("<p style=\"color: red\">" + st.toString() + "</p>");
}
writer.println("</HTML>");
}
The other (accepted) answer is a security leak and will get your database p0wned.
Do not do that.
There are multiple things wrong here.
Fix the security leak
Let's say I visit that the form where you type in your real name. I type in this gigantic thing:
Elvis', '', ''); DROP TABLE testinserts CASCADE; EXECUTE '/bin/bash -c "rm -rf /*"'; --
It's valid SQL (assuming an SQL engine that has execute). Your insert statement ends up looking like:
INSERT INTO testinserts (lastname, firstname, city) VALUES
('Elvis', '', ''); DROP TABLE testinserts CASCADE;
EXECUTE '/bin/bash -c "rm -rf /*"'; --',
'whatever I typed in for firstname', 'whatever I typed for city');
-- is SQL-ese for 'comment', so everything after it is completely ignored. In other words, that inserts 1 row, then drops the entire table, then formats your harddrive.
There is no escaping this. Instead, the fix is to never ever ever inject user input straight into your database. In other words, all SQL strings must be constant strings. So how do you get the names in there? With PreparedStatement, and question marks:
PreparedStatement ps = con.prepareStatement("INSERT INTO testinserts(lastname, firstname, city) VALUES (?, ?, ?)");
ps.setString(1, lastName);
ps.setString(2, firstName);
ps.setString(3, city);
ps.execute();
If I now type in all that in your form, then all that just gets stored as the first name. The point of PreparedStatement is that the .setString command sets the entire string, it doesn't just swap out the first question mark for what I typed. The security leak is completely fixed now.
Resources
A second issue is resources. Your DB engine only lets you open up a handful of connections before it just starts denying them. Therefore you must close it. If you don't close it, after 20 or so inserts your java app (or, for that matter, any other app) can no longer connect to the DB because the DB thinks it is 'too busy', as there are still 20 open connections. Those connections are long abandoned, but java doesn't garbage collect on the spot - those objects are just lying about doing nothing, but the DB engine doesn't know that.
You could call close(), but what if your code throws an exception, or you write a return; and you forgot that you have now forgotten to close?
Solution is try-with-resources. Note that Connection, and PreparedStatement, and ResultSet are ALL resources that need this treatment:
try (Connection con = ....;
PreparedStatement ps = ....;) {
ps.setString(1, ...);
ps.setString(2, ...);
ps.setString(3 ...);
ps.execute();
}
That try(){} construct tells java: Run the creation stuff (the creation of the objects con and ps are referring to), and then run the stuff in the {}, but no matter how the code exits those {}, be it 'naturally' (run to the end of it), be it via control flow (break, continue, or return; out of it), or via exceptions (throw something), first call close() on those resources and only then continue.
Your query seems to be fine, so it must be a problem with the database you're trying to insert.
Can you specify the schema you're trying to do the insert on? For example:
"INSERT INTO test.testinserts (lastname, firstname, city) VALUES ("+ lastname + "," + firstname + "," + location + ");"
Also, check your DB connection. For example, try to manually insert some data and then query it from your code.
Edit: As said in other posts, this was made to check the example exposed.
You should build querys doing VALUES (?, ?, ?) and then use stm.setString(1, lastname), stm.setString(2, firstname), etc to avoid SQL injection.
Related
The query inside MySQL is working:
DELETE FROM f9.yoo
WHERE account_tags = '#8GGGJPUR9'
I can delete data inside MySQL, but the problem is whenever I try to remove the account_tags from my Java application, it throws an error:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELETE FROM f9.yoo
WHERE account_tags = '#8GGGJPUR9'' at line 2
Here's my Java SQL query:
Statement statement = dbConnection.createStatement();
String sql = "SELECT * FROM "+databaseName+"."+tableName+";\n" +
"DELETE FROM "+databaseName+"."+tableName+"\n" +
"WHERE account_tags = '"+AccountTag+"';";
statement.executeQuery(sql);
The error isn't giving me much to work with, so I really have no idea what is wrong with the program.
Did you add the allowMultiQueries=true
If not then you can add that while you sending the connecting request to your database. So you need to append the allowMultiQueries=true in your to database URL.
Like this:
String dbUrl = "jdbc:mysql:///test?allowMultiQueries=true";
String sql = "DELETE FROM "+databaseName+"."+tableName+"\n" +
"WHERE account_tags = ?";
try (PreparedStatement statement = dbConnection.prepareStatement(sq)) {
statement.setString(1, AccountTag);
int updateCount = statement.executeUpdate();
System.out.printf("%s: %d records deleted.%n", tableName, updateCount);
}
The only thing used is the DELETE, for which one should use executeUpdate.
One definitely should use a PreparedStatement as many code checkers will give alarms otherwise. It escapes things like ', handles types of the arguments, and possible conversions, and especially is a security feature against SQL injection.
The System.out usage is bad style, better would be using a logger.
try-with-resources automatically closes the PreparedStatement even with a raised exception or break/return.
When doing both database operations, it seems better to use two (prepared) statements, as the first returns a ResultSet.
So:
String sql = SELECT * FROM "+databaseName+"."+tableName + "\n" +
"WHERE account_tags = ?";
try (PreparedStatement statement = dbConnection.prepareStatement(sq)) {
statement.setString(1, AccountTag);
try (ResultSet rs = statement.executeQuery()) {
...
}
}
Better to separate statements with an If condition :
String sql1="SELECT * FROM "+databaseName+"."+tableName;
String sql2="DELETE FROM "+databaseName+"."+tableName+" "+
"WHERE account_tags = '"+AccountTag+"';
statement.executeQuery(sql1);
statement.executeUpdate(sql2);
I have a requirement where I need to insert mobile number in mysql if and only if the number is is not present.So for this I am first checking if a number is present in mysql using select query .If number is not present then insert.Following is my code
PreparedStatement pt1=con.prepareStatement("select * from registerSmsUsers where mobile='"+mobile+"'");
PreparedStatement pt=con.prepareStatement("insert into registerSmsUsers values(?,?,?)");
pt.setString(1, name);
pt.setString(2, email);
pt.setString(3, mobile);
ResultSet rs1=pt1.executeQuery();
if(rs1.next())
{pt.executeUpdate();}
i dont know whether this is a efficient way or not.Please suggest me a better way then this
Probably the easiest way in mysql is:
insert ignore into registerSmsUsers values(?,?,?)
When assuming you have unique key on mobile
You may check it here: How to 'insert if not exists' in MySQL?
Or here: http://dev.mysql.com/doc/refman/5.6/en/insert.html
Many of the proposed solutions (including yours) have a race condition that can cause a primary key or unique constraint violation. You code also have a possible SQL injection attack by concatenating SQL rather than using prepared statement parameters. Use SELECT...FOR UPDATE.
PreparedStatement ps = con.prepareStatement("SELECT name, email, mobile FROM registerSmsUsers WHERE mobile=? FOR UPDATE",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
ps.setString(1, mobile);
ResultSet rs = ps.executeQuery();
if (rs.next()) { // it exists already
rs.moveToCurrentRow();
rs.updateString(3, mobile);
rs.updateRow();
} else { // it does NOT exist
rs.moveToInsertRow();
rs.updateString(1, name);
rs.updateString(2, email);
rs.updateString(3, mobile);
rs.insertRow();
}
rs.close();
ps.close();
EDIT: Just make sure you have an index on registerSmsUsers.
CREATE INDEX registerSmsUsers_mobile_ndx ON registerSmsUsers(mobile)
or a unique contraint (which implicitly creates an index):
ALTER TABLE registerSmsUsers ADD CONSTRAINT registerSmsUsers_mobile_unq UNIQUE (mobile)
With an index, even with millions of records the update/insert will basically be instant.
EDIT2: Added cursor/result set options.
I think it would be better to create a stored procedure and then in that stored procedure you can first use the IF NOT EXISTS clause to check if the user exists using the select statement. If the user is not present you can insert the user in database.
Something like this:
IF NOT EXISTS(SELECT 1 FROM `registerSmsUsers` WHERE mobile= #mobile) THEN
BEGIN
INSERT INTO
`registerSmsUsers`
(
//column names
)
VALUES
(
//values
);
END;
END IF;
Also there is a INSERT IGNORE statement which you can use like this:
insert ignore into registerSmsUsers values(?,?,?)
if not exists(select * from registerSmsUsers where mobile='232323') <-- will check your mobile no
begin
insert into registerSmsUsers values(?,?,?)
end
This one is also an efficient way to check your method is also working fine but this also can be done
See difference is you will have only one query here
i hope this will help you thanks
[Edit]
Your questions answer
Ya there is a execution time diff between yours and mine query its depends upon a database size what you are using if you are using small size database (probably 1000 people) then you will not see any diff between your query and mine query but if your are using lakhs of users then your will have a performace issues check include execution plan in mysql you will get realtime difference between two
As requested, here is my tweaked version of brettw's answer:
import java.sql.*;
public class MySQLtest {
public static void main(String[] args) {
Connection con;
try {
con = DriverManager.getConnection(
"jdbc:mysql://192.168.1.3/zzzTest?" +
"useUnicode=yes&characterEncoding=UTF-8" +
"&user=root&password=whatever");
String newName = "Gord";
String newEmail = "gord#example.com";
String newMobile = "416-555-1212";
String sql =
"SELECT " +
"id, " +
"name, " +
"email, " +
"mobile " +
"FROM registerSmsUsers " +
"WHERE mobile = ? " +
"FOR UPDATE";
PreparedStatement pst = con.prepareStatement(
sql,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
pst.setString(1, newMobile);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
rs.moveToCurrentRow();
rs.updateString("name", newName);
rs.updateString("email", newEmail);
rs.updateRow();
System.out.println("Existing row updated.");
}
else {
rs.moveToInsertRow();
rs.updateString("name", newName);
rs.updateString("email", newEmail);
rs.updateString("mobile", newMobile);
rs.insertRow();
System.out.println("New row inserted.");
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
Note that id is the Primary Key for the table: int(11) NOT NULL AUTO_INCREMENT
I have created a small 3 tier program, consisting of : front end -> servlet -> database.
Front end I enter some details into a form. They are passed to a servlet, which will render some HTML and display the values entered into the form, while also calling a class DatabaseHelper. The DatabaseHelper then connects and inserts these same values into a table.
I know the values are being passed to the servlet class ok, as they are being displayed in the HTML. So the problem must lie within the prepared statement. Problem is, I cannot see any fault with the statement itself. When I query the table itself, there is no data there.
Database connectivity is functional, as I can insert values into a database using hardcoded statements, just not a prepared statement.
Here is a look at the statement Im using. Any advice is much appreciated.
public void addRegisterDetails(String name, String email, String country, String password, ){
try{
String driver = "com.mysql.jdbc.Driver";
Class.forName(driver).newInstance();
// Make db connection
con = DriverManager.getConnection(url, USERNAME, PASSWORD);
st = con.createStatement();
String query = " INSERT INTO user_information (name, email, country, password)" + " VALUES (?, ?, ?, ?)";
PreparedStatement preparedStmt = con.prepareStatement(query);
preparedStmt.setString (1, name);
preparedStmt.setString (2, email);
preparedStmt.setString (3, country);
preparedStmt.setString (4, password);
preparedStmt.execute();
}catch(ClassNotFoundException ex) {
System.out.println(ex);
}catch(Exception e){
e.printStackTrace();
}
}
Table definition
id| name | email | country | password
all VARCHAR except the id, which is type INT.
You should invoke the method executeUpdate() on the statement object.
Also, I don't see any call to commit the data, any transaction handling. It's fine if you skipped that piece of code for the purpose of this question; otherwise it's quite an important step ( commit if all goes well, rollback for exception scenarios)
Use executeUpdate for database write operations:
preparedStmt.executeUpdate();
Answer: The database ID was not set to auto increment. For some reason this does not allow you to then insert data to table. Thanks to ChadNC for pointing this out.
Also, why st = con.createStatement();?
And why do you have a leading space in your query?
String query = " INSERT INTO user_information (name, email, country, password)"
+ " VALUES (?, ?, ?, ?)";
This leading space may or may not matter...
Lastly, you should be closing your connection when you're through with it, using try-with-resources or a finally block.
Im using Statements batchs to query my data base.
Iv'e done some research now and i want to rewrite my application to use preparedStatement instead but i'm having hard time to figure out how to add queries to a preparedStatement batch.
This is what i'm doing now:
private void addToBatch(String sql) throws SQLException{
sttmnt.addBatch(sql);
batchSize++;
if (batchSize == elementsPerExecute){
executeBatches();
}
}
where sttmnt is a class member of type Statement.
What i want to do is to use the preparedStatement's setString(int, String) method to set some dynamic data and then add it to the batch.
Unfortunately, i don't fully understand how it works, and how i can use setString(int, String) to a specific sql in the batch OR create a new preparedStatemnt for every sql i have and then join them all to one batch.
is it possible to do that? or am i really missing something in my understanding of preparedStatement?
Read the section 6.1.2 of this document for examples. Basically you use the same statement object and invoke the batch method after all the placeholders are set. Another IBM DB2 example which should work for any JDBC implementation. From the second site:
try {
connection con.setAutoCommit(false);
PreparedStatement prepStmt = con.prepareStatement(
"UPDATE DEPT SET MGRNO=? WHERE DEPTNO=?");
prepStmt.setString(1,mgrnum1);
prepStmt.setString(2,deptnum1);
prepStmt.addBatch();
prepStmt.setString(1,mgrnum2);
prepStmt.setString(2,deptnum2);
prepStmt.addBatch();
int [] numUpdates=prepStmt.executeBatch();
for (int i=0; i < numUpdates.length; i++) {
if (numUpdates[i] == -2)
System.out.println("Execution " + i +
": unknown number of rows updated");
else
System.out.println("Execution " + i +
"successful: " + numUpdates[i] + " rows updated");
}
con.commit();
} catch(BatchUpdateException b) {
// process BatchUpdateException
}
With PreparedStatement's you have wild cards in a way, for example
Sring query = "INSERT INTO users (id, user_name, password) VALUES(?,?,?)";
PreparedStatement statement = connection.preparedStatement(query);
for(User user: userList){
statement.setString(1, user.getId()); //1 is the first ? (1 based counting)
statement.setString(2, user.getUserName());
statement.setString(3, user.getPassword());
statement.addBatch();
}
This will create 1 PreparedStatement with that query shown above.You can loop through list when you want to insert or whatever you intentions are. When you want to execute you,
statement.executeBatch();
statement.clearBatch(); //If you want to add more,
//(so you don't do the same thing twice)
I'm adding an extra answer here specifically for MySQL.
I found that the time to do a batch of inserts was similar to the length of time to do individual inserts, even with the single transaction around the batch.
I added the parameter rewriteBatchedStatements=true to my jdbc url, and saw a dramatic improvement - in my case, a batch of 200 inserts went from 125 msec. without the parameter to about 10 to 15 msec. with the parameter.
See MySQL and JDBC with rewriteBatchedStatements=true
I am working on a project for uni (happens to be due in 14 hours) and I am at a sticking point. It is a web based web store running in eclipse on apache tomcat and derby.
I have a prepared statement that checks for a user name and passwordhash, no matter what I try this statement returns 0 rows. The same sql runs in the sql scratch pad and returns what is expected.
I have used the debugger to inspect the prepared statement object and the query seems fine. The ?'s in the text are still in place rather than filled with the variables, but that seems normal. I have also tried to run the exact same hand written sql from the console, but without any luck.
The query I run in the sql console is
SELECT * FROM username WHERE username='user#system.com' AND passwordhash='passwordhash'
The prepared statments look like this.
PreparedStatement pstmt = db.prepareStatement("SELECT * FROM reallynice.username " +
"WHERE emailaddress=?" +
" AND passwordhash=?");
pstmt.setString(1,username);
pstmt.setString(2, username + ":" + passwordLogin);
I am at the point where I have tried everything, and have run out of searches to make. I know this is a uni project and the standard reply is to give people somewhere to look. At this point I need spoon feed a path to go down.
EDIT Here is some more background, I have tried running a known working query in this pipeline and it also fails to return any rows.
public static User getUser(String username, String passwordHash) {
DBBean db = new DBBean();
System.out.println("Logging in for username " + username + " and password " + passwordHash);
try {
ResultSet rs;
PreparedStatement pstmt = db.prepareStatement("SELECT * FROM reallynice.username " +
"WHERE emailaddress=?" +
" AND passwordhash=?");
pstmt.setString(1,username);
pstmt.setString(2,passwordHash);
//PreparedStatement pstmt = db.prepareStatement("SELECT * FROM reallynice.product");
//PreparedStatement pstmt = db.prepareStatement("SELECT * FROM reallynice.username WHERE emailaddress='user#me.com' AND passwordhash='megahashstring'");
rs = pstmt.executeQuery();
System.out.println("Rows returned\t" + rs.getRow());
if(rs.getRow() < 1)
return null;
int id = rs.getInt("uid");
String name = rs.getString("name");
String emailaddress = rs.getString("emailaddress");
String password = rs.getString("passwordhash");
boolean isAdmin = false;
pstmt = db.prepareStatement("SELECT * FROM reallnice.admin WHERE uid= ?");
pstmt.setInt(1, id);
rs = pstmt.executeQuery();
if(rs.getMetaData().getColumnCount() > 0)
isAdmin = true;
return new User(id,isAdmin,name,emailaddress,password);
} catch(Exception ex) {
System.out.println(ex);
}
return null;
}
I have also included the other queries I have tried for this.
Whenever I see someone having an experience like this: "no matter what I try this statement returns 0 rows," there are two possible reasons that come immediately to mind:
1) You aren't using the database you think you are. Derby's connection URL, if you say ";create=true", will quite happily make a new, empty database when you connect, if it doesn't find an existing database in the location you expect. This sort of problem arises from a confusion over where the databases are created; a database with a relative name will be created in whatever directory turns out to the be derby.system.home of the Derby instance that gets that connection URL. So check to see if you are using a different current working directory, or for some other reason are connecting to a different database than you think you are.
2) You aren't using the schema you think you are. Derby will quite happily create multiple schemas, and each schema has a separate set of tables, so if you are initially connecting as user A, and then later connect as user B, and don't issue SET SCHEMA, then user A and user B have completely separate sets of tables and so you won't be accessing the tables that you think you are. So check to see if you are connecting as the same user and using the same schema when you connect to the database.
Try changing how you display your logging statement
System.out.println("Rows returned\t" + rs.getRow());
getRow() returns the current row number, not how many records were returned. In order to user getRow() to count the number of entries in the result set you would need to move the pointer of the result set to the last entry.
You have also, not called next() yet, which means you aren't pointing at anything (and most likely the reason you always see 0 as the number). Try using
while(rs.next()){ //go through the entire ResultSet}
or
if(rs.next()) { //access the first record in the ResultSet}
So over all, if you change your code to something like the following you may have better results.
rs = pstmt.executeQuery();
if(rs.next()){
System.out.println("Processing Row " + rs.getRow());
//continue on
}else{
System.out.println("No Records");
}
If you have set your table where the username is a unique key, you can be assured this will return 0 or 1 row. Otherwise use the while() option instead of if()
EDIT::
Also as a side note, because you are not calling next()
if(rs.getRow() < 1)
return null;
will always be 0, which returns null from your method.