This section will detail three common issues with JDBC, just to save you the trouble of puzzling through them yourself. The first is fairly simple. ResultSets returned from an executeQuery call always start out before the first row returned. That means that you must advance to the first row returned before trying to fetch information from a ResultSet by calling the "next" method:

ResultSet newSet = null;

try {
    newSet = s.executeQuery("SELECT * FROM book");
} catch (SQLException se) {
    System.out.println("We got an exception while executing our query- that probably means our SQL is invalid.");
    se.printStackTrace();
    System.exit(1);
}

try {
    String value = newSet.getString(1); // BAD- we haven't called next() yet
} catch (Exception e) {
    System.out.println("We'll get an exception here because we haven't stepped to the first row of the ResultSet yet.");
    e.printStackTrace();
}

try {
    newSet.next();
    String value = newSet.getString(1);
} catch (SQLException se) {
    System.out.println("We'll only get an exception here if we've lost our connection, which isn't our fault.");
    se.printStackTrace();
    System.exit(1);
}

The next issue is also related to ResultSets, but it's far simpler. You can't get the number of rows returned without stepping through the ResultSet using "next" and incrementing a counter. This is due to the fact that JDBC doesn't necessarily fetch the next row(or, for that matter, whether or not there is a next row) until after you call "next".

The last is just a caveat. In a multi-threaded environment, it's good to ensure that each thread has its own Statements and ResultSets. That's because there is some state maintained in these objects, and using them from different threads will corrupt that state.