tags:

views:

229

answers:

5

Hello

I'm not sure why, but for some reason, the following code skips the else condition. I've tried just about everything I can think of, including switching the code blocks, but it still skips the else part. Basically, I want this method to return String temp = "no" if String docID that is passed to the method is not found in the FILESTATUS database, and String temp = "yes" if it is found.

static String checkDocID(String docID)
{
    String temp = null;

    System.out.println("Checking if data already exists in database...");
    try
    {
        Main.stmt = Main.con.createStatement();
        String command = "SELECT * FROM FILESTATUS WHERE ID='" + docID + "'";
        ResultSet queryResult = Main.stmt.executeQuery(command);
        if (!queryResult.next())
        {
            temp = "no";
        }
        else
        {
            while (queryResult.next())
            {
                String result = queryResult.getString("ID");
                if (result.equals(docID))
                {
                    temp = "yes";
                    break;
                }
            }
        }
        Main.stmt.close();
    }
    catch (Exception ex) {ex.printStackTrace();}
    return temp;
}

Thanks!

+6  A: 

Might be better to restructure this loop as:

static String checkDocId(String docId) {
    String temp = "no";

    while (queryResult.next()) {                
        String result = queryResult.getString("ID");

        if (result.equals(docID)) {
            temp = "yes";
            break;
        }
    }

    return temp;
}

Some people don't like using break (I usually don't) so you can use a boolean in your while (I find that it reads more like english and you can tell the terminating condition right from the while instead of looking for an if inside):

static String checkDocId(String docId) {
    boolean found = false;

    while (queryResult.next() && !found) {
        String result = queryResult.getString("ID");
        found = result.equals(docID);
    }

    return found ? "yes" : "no";
}

You're performing a needless comparison otherwise. Remember, a while is just an if with a goto at the end ;)

As far as your problem is concerned, what Paul said is correct. Eitherway, I would still restructure the loop so that it's more elegant.

Vivin Paliath
why not just `return "yes"` as soon as result is found.
irreputable
That would mean you need another `return` outside the loop. I'm not a fan of multiple `return`s unless absolutely necessary. 9 times out of 10, you can rewrite your algorithm to be more elegant and remove the use of multiple `return`s.
Vivin Paliath
+16  A: 

Because you end up calling queryResult.next() in the "if" and again in the while, you're skipping the first result. If there is only one result, the while loop will never execute.

If I can make a couple of suggestions:

  • Use bound variables in a PreparedStatement rather than putting "docID" in the query string
  • Don't test result.equals(docID) since the query already assured that.
  • prefer boolean to String "yes" or "no"
  • set the result to "no" or false, then set it to "yes" or true in the loop. The extra assignment is probably faster than the extra test, plus you can skip the do{}while which most people find harder to read.
Paul Tomblin
concise and correct
Andrew Duffy
it's probably worth pointing out that `ResultSet.next` is not like `Iterator.hasNext`
Andrew Duffy
Wow - I figured it was just a case of my logical brain taking a vacation. Solved it using a `do{}-while{}` as opposed to just a `while{}`, as suggested by splix.Thank you!
ryantmer
I am surprised that this isn't accepted. It covers all of the unnecessary clutter in the code logic (expect of closing in finally). Maybe because you omitted the code sample?
BalusC
A: 

rs.next() rolling current cursor after each call

try with

static String checkDocID(String docID)
{
    String temp = null;

    System.out.println("Checking if data already exists in database...");
    try
    {
        Main.stmt = Main.con.createStatement();
        String command = "SELECT * FROM FILESTATUS WHERE ID='" + docID + "'";
        ResultSet queryResult = Main.stmt.executeQuery(command);
        boolean found = queryResult.next();
        if (!found)
        {
            temp = "no";
        }
        else
        {
            do {
                String result = queryResult.getString("ID");
                if (result.equals(docID))
                {
                    temp = "yes";
                    break;
                }
            } while (queryResult.next())
        }
        Main.stmt.close();
    }
    catch (Exception ex) {ex.printStackTrace();}
    return temp;
}
splix
This works perfectly - just changing it to do {} while {} was all it needed.Thanks!
ryantmer
It may work, but this code would be what others might call a minor WTF. Paul gave the real answer to the problem and vivin has a much better code implementation.
Tim Bender
Agree with Tim regarding the WTF'ness of the accepted solution.
Vivin Paliath
Although vivin's would work too, I only had to change two lines to get this (and I'm lazy!). Are there any major problems with this code? (If it makes any difference, stability is more important than speed/efficiency with this code.) Thanks!
ryantmer
If you want your code to be stable and maintainable then I would not go with the current solution. Using the current structure is what led you to this problem in the first place. Using the `while` without the `if` is far more maintainable. Laziness is not an excuse. You cannot be lazy and write stable/maintainable code!
Vivin Paliath
+1  A: 

You are calling queryResult.next twice before you are trying to read it. To prove this, put a println or something right after the else (before the while).

Since you are selecting by a particular ID, moving to next() twice will in fact fail the second time (presumably) and never execute the while block.

Bob Robinson
+3  A: 

After a bit astonishment about the code (leaking DB resources, not trusting the DB that it returns the right docID, doing a SELECT * while you don't need all of the columns at all, not taking benefit of SQL-injection-sanitization powers of PreparedStatement, using String instead of boolean to denote a true/false value, a messy code flow), here's how it should really be done instead:

private static final String SQL_EXIST_DOCID = 
    "SELECT id FROM filestatus WHERE id = ?";

public boolean exist(String docID) throws SQLException {
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    boolean exist = false;

    try {
        connection = database.getConnection();
        statement = connection.prepareStatement(SQL_EXIST_DOCID);
        statement.setString(1, docID); // Shouldn't it be a Long instead? Characterbased PK's are *far* from efficient.
        resultSet = statement.executeQuery();
        exist = resultSet.next();
    } finally {
        if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
        if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
        if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
    }

    return exist;
}

Clear, concise and simple as that. For more insights about using basic JDBC the right way you may find this article useful. I hope that you take it serious to learn from it. Really.

BalusC
I personally like your answer the best but to be pedantic there is no good reason to put the connection creation inside the try/catch and at least two reasons not to. 1) it makes the finally block needless complicated for connection, 2) if the try {} block gets large and some joker manages to set connection = null then you'll never close it... versus getting an NPE.
PSpeed
1) can be refactored away, 2) is a programmer error. Certainly the connection should be closed in the finally, else you're leaking resources.
BalusC
Although I cannot use your code exactly (as the connection is already established at this point, and needs to remain open past this point), you did open my eyes to several problems with my code... A software engineer I am not!Thanks!
ryantmer
You're welcome. You can in theory also replace `getConnection()` by `getTransaction()`.
BalusC
@BalusC, yes the connection should be closed in the finally but it should not be created in the try... if you get an exception from create there will _never_ be a connection to close. Why create extra complexity/problems?
PSpeed
@PSpeed: the connection still needs to be closed if its creation succeeds but the creation of statement or resultSet not.
BalusC
@BalusC, move connection = database.getConnection() above the try {... then don't bother checking for connection != null in the finally... just close it.
PSpeed
@PSpeed: that's true when you leave out the `catch` on `SQLException` from the `try` block. But sometimes you'd like to add the `catch` so that you can do some logging and/or wrap it more abstractly in a custom `DAOException` or something like that.
BalusC
Yeah, I can only comment on the methods that exist... not the hypothetical ones that architected differently. :) Given the code, if I wanted to catch and wrap the exception I'd probably still push this code into a method that throws SQLException and just wrap the method. It tends to be cleaner, and since we're talking about hypothetical future changes, you never know what other exceptions you might want to catch and log and keeping the SQL code together is a good thing.
PSpeed