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.
Related
try{
Statement stm = conn.createStatement();
String sql = "SELECT * from BOOKS WHERE ISBN_No = '" + line + "'";
ResultSet rs = stm.executeQuery(sql);
if(//values are returned) {
displayBookInfo(line);
}
else (//if it is null) {
System.out.println("No book found");
}
stm.close();
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Fail to search the book" + line );
noException = false;
}
After I execute the ResultSet rs = stm.executeQuery(sql); I want to check if the query returned a value or if it was empty so that I can execute either "display book details" or "no book found message". I am just confused about how I should compare and how comparison works.
This code is a security leak. You must fix this first.
You cannot include untrusted inputs in a query like this. What if someone enters, say:
1234'; DROP TABLE books CASCADE; EXECUTE 'FORMAT C: /Y'; --
In the web form? Don't try it, you'll wipe your disk. You get the point, surely.
The right way is to use stm.prepareStatement(sql), where sql is a constant (so not something you insert user entered stuff into), using a ? where user input is needed, then calling .setString(1, line) to then tell your db driver what should go in place of the question mark.
Then, simply rs.next(), which advanced to the next row in the result (first call advances to the first row). If there are no rows left, it returns false instead. Hence, if your query returns 0 rows, the first resultSet.next() call returns false right away.
Your code also fails to close. You must use try-with-resources on everything (ResultSet, (Prepared)Statement, and most importantly the Connection), or your app will crash after a few statements.
NB: Minor nit, if all you want to know is if there's at least one result, add LIMIT 1, and just SELECT 1 FROM instead - it's less overhead that way.
I am trying to check if a player is already is in the database with this code:
Statement sql = mySql.getConnection().createStatement();
ResultSet check = sql.executeQuery("SELECT * FROM `playerinfo` WHERE Username='" + player.getName() + "';");
System.out.println(check.toString());
if(check != null) {
System.out.println("2");
Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Player already in database");
check.close();
sql.close();
return;
}
I checked but nothing is in the database and it says that the player already contains in the database
Sorry for bad english
Some considerations:
When checking whether the database contains a certain value, it's good practise to do this using a query that returns a single value (and not SELECT * which returns all columns of all rows that match the WHERE condition). You can do this e.g. by selecting a single check flag (SELECT 1) with a row-limiting clause (LIMIT 1):
SELECT 1 FROM playerinfo WHERE Username = ? LIMIT 1
This query is guaranteed to return only one row (with a single column, '1') if a player with the given name exists, or no rows if there are no players with the given name.
As others have pointed out, when you're inputting parameters into the query, you should use a PreparedStatement instead of a simple statement with concatenated inputs. This way, you can avoid SQL injection and the database is also able to reuse/cache the query (or cursor) internally.
Finally, you should close the resources you use, even if an Exception gets thrown during the execution. This is best done in the finally clause, or if you're on Java 7 or later, using the try-with-resources statement.
With these things in mind, a re-write of your code could look like this:
PreparedStatement ps = null;
try {
ps = mySQL.getConnection()
.prepareStatement("SELECT 1 FROM playerinfo WHERE Username = ? LIMIT 1");
ps.setString(1, player.getName());
ResultSet rs = ps.executeQuery();
// the first invocation of rs.next() returns true if
// there are rows in the result set, or false if no rows were found
if (rs.next()) {
System.out.println("2");
Bukkit.getConsoleSender().sendMessage(ChatColor.RED
+ "Player already in database");
}
rs.close();
} finally {
if (ps != null) {
ps.close();
}
}
I think instead of checking if the ResultSet is null or not, you should check if the ResultSet contains any row or not.
Apart from that, use PreparedStatements.
I am creating a java program, with which I am editing into a Microsoft Access Database. I have a certain case, in which I need to search if a certain record already exists in my table, if it does, I want to update it, and if not, I want to create it from scratch.
I have found this piece of code:
IF EXISTS (SELECT * FROM USERS WHERE USERID=#UID) select 1 else select
This code gives me an error, saying that a SELECT, UPDATE or DELETE statement was expected.
In a code that I have tried my self, I have done the following:
try{
s = con.createStatement();
s.executeQuery("SELECT * FROM table WHERE date='" + today + "'");
rset = s.getResultSet();
if (rset.getString("date") == null){
s = con.createStatement();
s.executeUpdate("INSERT INTO table VALUES ('" + today + "','" + cflow + "','" + vat + "','" + cnn + "')");
}
}
catch (SQLException exp)
{
System.err.println(exp);
}
But with this code, when the record does not exist yet, the user input is not updated inside the database.
Thanks for your time :)
1st: If I can remember right, then is
IF EXISTS (SELECT * FROM USERS WHERE USERID=#UID) select 1 else select
an incomplete transact sql statement -used by the sql engine from a database system.
2nd:
if (rset.getString("date") == null){}
you should avoid this way, because there is a good chance to get a Nullpointer Exception.
In my eyes a better one is a test the size of resultset for zero or the resultset it self for the value of NULL.
In case the UPDATE statement won't also be executed, check your SELECT statement using the database engine -Ms Access, SQL Server, etc.- directly. The advantage is you can exclude a mistake in your SELECT query.
What about this?
SELECT IF EXISTS (SELECT * FROM USERS WHERE USERID=#UID) THEN 1 ELSE 0 END
or
SELECT IF(EXISTS (SELECT * FROM USERS WHERE USERID=#UID), 1, 0)
(I'm not sure about the real syntax here.)
(rset.getString("date") == null)
should be
(!rset.next())
rset is positioned 'before' the first result that gets returned. next() returns true if there was a 'next' result to get.
Also, what datatype is your 'date' variable? There's no guarantee that a date.toString() will format the date correctly for MS-Access version of SQL.
Rather, prepare a statement
PreparedStatement ps = connetion.prepareStatement("SELECT * from table where date=?");
and set the date like
ps.setDate(1, date);
then issue the query using the prepared statement.
That saves any toString() issues. (I haven't compiled this, it almost certainly won't work as-is, but the idea is there).
Here is what i used to find the last ID in a table. IF the table is empty the no ID will be returned. If table is populated then i needed the next ID for new record.
ResultSet mn = stmt.executeQuery("SELECT MAX(ExamID)FROM ExamResults");
if (mn == null){
jTextField1.setText("1");
} else{
while (mn.next()) {
int lastID =Integer.parseInt(""+(mn.getObject(1)));
jTextField1.setText(""+(lastID+1));
}
}
// close the objects
mn.close();
stmt.close();
conn.close();
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'm making a db call as follows:
String sqlAlert = "SELECT * FROM demotable where demo_no ='"
+rsDemo.getString("demo_no") + "'";
ResultSet rsAlert = db.GetSQL(sqlAlert);
if (rsAlert.next()) {
String newAlert = rsAlert.getString("cust3")+"1";
String newAlertSql = "UPDATE demotable SET cust3 = '" + newAlert + "' where demo_no='" + rsDemo.getString("demo_no") + "'";
System.out.println("Update alert msg: " + newAlertSql);
db.RunSQL(newAlertSql);
} else {
System.out.println("empty result. Demo_no = "+rsDemo.getString("demo_no"));
String sqlAlertinsert = "INSERT INTO demotable VALUES('" + rsDemo.getString("demo_no") + "','','','','','<unotes></unotes>')";
db.RunSQL(sqlAlertinsert);
System.out.println("insert demo done");
String sqlAlert2 = "SELECT * FROM demotable where demo_no ='"rsDemo.getString("demo_no") + "'";
ResultSet rsAlert2 = db.GetSQL(sqlAlert2);
if (rsAlert2.next()) {
String newAlert = rsAlert2.getString("cust3")+"1";
String newAlertSql = "UPDATE demotable SET cust3 = '" + newAlert+ "' where demo_no='" + rsDemo.getString("demo_no") + "'";
System.out.println("Update alert msg: " + newAlertSql);
db.RunSQL(newAlertSql);
}
rsAlert2.close();
}
rsAlert.close();
rs.close();
I am trying to insert rows into demographiccust if rsAlert returns an empty set and then access values from it. But my code returns this exception "Illegal operation on empty result set" around "if (rsAlert2.next()) { ". Why does it return an empty set even after inserting values into the table? Please help. Thank you.
It may be because of the open cursor. You must close your first Statement, prior trying the second. ResultSet is a connected thing, when you close the Statement it get closed too. I can't see the implementation of your db.RunSQL() and db.GetSQL() methods.
However, I am having the suggestion on how you should do it, in the first place. Here you go,
Update it without querying the database
Check how many rows updated. If none, then step 3, otherwise completed
Insert the record with the correct values in the first place. No need to update it after inserting.
Tips:
Try using PreparedStatement, instead
Try to stick with Java Naming Convention
Try using meaningful names, i.e. for example your method db.GetSQL() is not returning an SQL, but contrarily asking one, and in fact returning a ResultSet.
Never return a ResultSet. This may lead to bloated code and a lot of open cursors. Don't make the user of your method to close it. Close it yourself in your method where you are performing any database query, and return the result as a bean or a list of beans.
It's just a guess, but because you are interpolating rsDemo.getString("demo_no") directly into the SQL, you may be passing an SQL statement that isn't what you want. Try using the parameter binding api.