JDBC catching login failure exception and re-promt

2019-09-09 19:29发布

问题:

try{
Class.forName ("oracle.jdbc.driver.OracleDriver"); // identify

Connection con = DriverManager.getConnection
 ("jdbc:oracle...",userid,password); 


// create Statement and execute sql statement after
} catch (SQLException ex) {
        Logger.getLogger(Transcript.class.getName()).log(Level.SEVERE, null, ex);
    }

The only exception I can find is SQLException, but how can it distinguish from login failure and incorrect sql statement?

I want it to re-promts the user for another chance of entering id and password, do I put that in the catch block?

if so, do i need another nested try-catch to handle the exception?

As I want to handle the two situations separately, but it seems like there is only a SQLException I can use.

回答1:

Actually, you'll get a few more:

If you don't have the right Oracle driver in the classpath in Class.forName:
Exception in thread "main" java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver

If DriverManager.getConnection fails because of a wrong jdbc url syntax:
Exception in thread "main" java.sql.SQLException: No suitable driver found for jdbc:oracle...

If login/password fails at DriverManager.getConnection
Exception in thread "main" java.sql.SQLException: ORA-01017: invalid username/password; logon denied

In case of an incorrect SQL statement:
Exception in thread "main" java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist

In case of an incorrect SQL statement:
Exception in thread "main" java.sql.SQLSyntaxErrorException: ORA-00900: invalid SQL statement

In case of an incorrect SQL statement:
Exception in thread "main" java.sql.SQLException: Invalid column name

You can separate the login failure from SQL failures because they happen at different place in your code.

public class Oracle {

    public static void main(final String[] args) throws ClassNotFoundException, SQLException {
        final Connection con;

        try {
            Class.forName ("oracle.jdbc.driver.OracleDriver"); 
        } catch (final Exception e) {
            throw new RuntimeException("Driver failure");
        }

        try {
            con = DriverManager.getConnection ("jdbc:oracle:thin:schema/password@host:port:sid");
        } catch (final Exception e) {
            throw new RuntimeException("Login failure");
        }


        try {
            final Statement stmt = con.createStatement();

            final String sql = "select 1 from dual";
            final ResultSet rs = stmt.executeQuery(sql);
            while(rs.next()) {
                // do something with the data
            }
            rs.close();
        } catch (final Exception e) {
            throw new RuntimeException("SQL failure");
        }
    }
}

Note: it is a brute-force example, in a production code you may need more precise exception handlers, and you shall ensure that resources are always closed. For example, in case of an SQL error while reading a resut set and having an error because of referring to an invalid column, you may need to close the statement as well as the result set.

Two more Oracle-specific hints:

  • Do not pass login and password as a param in DriverManager.getConnection. An Oracle connection has a lot more parameters, it is easier to construct a JDBC url which contains everything: "jdbc:oracle:thin:schema/password@host:port:sid"

    • Note that host:port is needed. Many times multiple Oracle server instances can share the same port. You can use the service id (SID) to distinguish amongst them.
  • In case of a real Oracle exception, the exception is wrapped to an SQLException or an SQLSyntaxErrorException, sometimes this, sometimes that. I found pretty handy to put an own logic on top of this:

    • I catch any exception with try..catch. If the error message starts with ORA-, then it is an Oracle error, and I wrap it to a custom exception class and re-throw.

    • It might be handly to parse the Oracle error code in your custom exception. You can use that to differentiate between custom Oracle exceptions (-20000..-20999) what you throw in your PL/SQL (and can signal business level error). The rest of error codes always tell a technical error, i.e. something is wrong in your code or in the database structure.

To close a resource (pre-java7), use try..finally. Note the pessimistic handling of possible further exceptions when actually closing a resouce fails.

Statement stmt=null;
ResultSet rs=null;
try {
    try {
        final Statement stmt = con.createStatement();
        final String sql = "select 1 from dual";
        rs = stmt.executeQuery(sql);
        while(rs.next()) {
            // do something with the data
        }
        rs.close();
        rs=null;
        stmt.close();
        stmt=null;
    } catch (final Exception e) {
        throw new RuntimeException("SQL failure");
    }
} finally {
    if (rs!=null) {
        try {
            rs.close();
        } catch (Exception e) {
            // ignore - we can't do too much
        }
    }
    if (stmt!=null) {
        try {
            stmt.close();
        } catch (Exception e) {
            // ignore - we can't do too much
        }
    }
}

To close the resources on Java7 and above, you can benefit of the try with resource block. See How should I use try-with-resources with JDBC?



标签: java sql jdbc