Как я могу обработать это исключение в этом методе? - PullRequest
0 голосов
/ 16 ноября 2010

У меня есть код соединения JDBC, похожий на следующий из учебника по Java JDBC:

public static void viewTable(Connection con) throws SQLException {
    Statement stmt = null;
    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from " + dbName + ".COFFEES";
    try {
      stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        int supplierID = rs.getInt("SUP_ID");
        float price = rs.getFloat("PRICE");
        int sales = rs.getInt("SALES");
        int total = rs.getInt("TOTAL");
        System.out.println(coffeeName + "\t" + supplierID + "\t" + price + "\t" + sales + "\t" + total);
      }
    } catch (SQLException e ) {
      JDBCTutorialUtilities.printSQLException(e);
    } finally {
      stmt.close();
    }
  }

Моя проблема с этим способом обработки соединения состоит в том, что он закрывает оператор в блоке finally иМетод генерирует любое SQLException, которое может произойти.Я не хочу этого делать, потому что я хочу, чтобы любые проблемы решались в этом классе.Однако я делаю хочу, чтобы Statement#close() вызывал в блоке finally, чтобы он всегда был закрыт.

Сейчас я помещаю этот код в отдельный метод, который возвращает HashMap полей, возвращаемых к тому, что исключение обрабатывается в классе.Есть ли другой, возможно, лучший способ справиться с этим?

РЕДАКТИРОВАТЬ: close() SQLException является то, что меня интересует.Если возможно, я бы хотел справиться с этим в методе.Я мог бы написать попытку / поймать в окончании, но это кажется действительно неловким.

Ответы [ 4 ]

3 голосов
/ 16 ноября 2010

У вас есть несколько проблем:

  • Вам необходимо явно закрыть ResultSet. Некоторые драйверы меньше прощают, что забывают закрыть ResultSet, чем другие, и ничто не мешает быть уверенным, что он закрыт.

  • Вы должны поймать SQLException, генерируемое Statement.close, потому что это неинтересно и служит только для маскировки интересного исключения (если у вас есть что-то в этом методе, генерируйте исключение, тогда finally генерирует исключение на выход, вы получаете исключение из блока finally и теряете первое исключение). На самом деле ничего нельзя сделать, если при вызове метода close возникает исключение, просто зарегистрируйте его и продолжайте, это не то, о чем следует беспокоиться.

  • Вы должны отказаться от идеи обработки всех sqlexceptions в этом методе, SQLException, выдаваемого Statement.executeQuery, является тем, который стоит и должен распространяться, если что-то идет не так. Вероятно, другой код в вашем приложении захочет узнать, был ли ваш sql здесь успешным или нет, и именно для этого создаются исключения.

Лично я бы предложил для этого использовать такую ​​библиотеку, как Ibatis или spring-jdbc. JDBC подвержен ошибкам и утомителен, поэтому лучше воспользоваться существующими инструментами.

1 голос
/ 16 ноября 2010

Существует много способов записать инициализацию и закрытие JDBC, чтобы избежать использования котельной пластины.Однако, чтобы ответить на ваш вопрос, вы можете заключить stmt.close () в блок try-catch, как показано ниже.Кроме того, вам нужно закрыть свой набор результатов.(не написано ниже) Вы можете рассмотреть SpringDAO или Hibernate вместо JDBC, чтобы избежать проверенных исключений.

public static void viewTable(Connection con) throws SQLException {
    Statement stmt = null;
    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from " + dbName + ".COFFEES";
    try {
      stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        int supplierID = rs.getInt("SUP_ID");
        float price = rs.getFloat("PRICE");
        int sales = rs.getInt("SALES");
        int total = rs.getInt("TOTAL");
        System.out.println(coffeeName + "\t" + supplierID + "\t" + price + "\t" + sales + "\t" + total);
      }
    } catch (SQLException e ) {
      JDBCTutorialUtilities.printSQLException(e);
    } finally {
      try{
         stmt.close();
      }catch(Exception e) { /*LOG to indicate an issue */}
    }
  }
1 голос
/ 16 ноября 2010

Вот как я обычно обращаюсь с такого рода ресурсами (обратите внимание, что независимо от того, что вы делаете, вам нужна проверка stmt != null перед вызовом stmt.close!):

SomeResource resource = null;
try {
    resource = /* ...get the resource... */;

    /* ...use the resource... */

    // Close it    
    resource.close();
    resource = null;

    // ...maybe do some post-processing... */

} catch (SomeException se) {
    // code to handle SomeException
} catch (SomeOtherException soe) {
    // code to handle SomeOtherException
} finally {
    if (resource != null) {
        try {
            resource.close();
        } catch (IOException e) {
        }
    }
}

... хотя мой finally блок обычно намного проще, потому что у меня есть служебные методы для его инкапсуляции.(В частности, это, вероятно, выглядело бы так:

finally {
    resource = Utils.silentClose(resource);
}

... где silentClose проверяет и закрывает вызов !null, маскируя любое исключение, и всегда возвращает null.)

Ключевые аспекты вышеперечисленного:

  1. resource либо открыт, либо null, никогда !null, но закрыт (кроме кратко между вызовом close и null; я предполагаю, что вы не будете делиться этим с другими потоками).
  2. В обычном потоке я обычно вызываю close, не скрывая никаких исключений, которые он может выдать.
  3. В предложении finally, если resource равно !null, по определению произошло исключение.Поэтому я должен попытаться закрыть resource, но не допустить, чтобы выбрасывалось любое исключение и маскировалась фактическая вещь, которая пошла не так.

В частности, я оставляю ресурс открытымдо тех пор, пока он мне нужен в основном коде, для удобства чтения.

Для этого есть и другие идиомы:

  • Идиома «переброски»: всегда перехватывайте все исключения, закрывайте ресурсыи отбросить исключение.На мой взгляд, приводит к большому количеству ненужного кода.
  • Идиома «флаг успеха»: установите флаг - возможно, ваше возвращаемое значение - который сообщает вам, что все работает, а затем всегда очищайте в finally.Дело в том, что вы получаете то же самое дублирование, что и с моим кодом, если только вы не всегда будете скрывать исключения на close.Что приводит нас к:
  • Идиома «Мне наплевать на исключения при закрытии»: всегда делайте «тихое» закрытие.Гак.: -)

Применение вышеуказанного к вашему коду:

public static void viewTable(Connection con) throws SQLException {
    Statement stmt = null;
    ResultSet rs   = null; // <== You need this outside the try/catch block
    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from " + dbName + ".COFFEES";
    try {
        stmt = con.createStatement();
        rs = stmt.executeQuery(query);
        while (rs.next()) {
            String coffeeName = rs.getString("COF_NAME");
            int supplierID = rs.getInt("SUP_ID");
            float price = rs.getFloat("PRICE");
            int sales = rs.getInt("SALES");
            int total = rs.getInt("TOTAL");
            System.out.println(coffeeName + "\t" + supplierID + "\t" + price + "\t" + sales + "\t" + total);
        }

        // Explicit close, allows for exception since we won't be hiding anything
        rs.close();
        rs = null;
        stmt.close();
        stmt = null;

        // Possible further processing...

    } catch (SQLException e ) {
        JDBCTutorialUtilities.printSQLException(e);
    } finally {
        // Close the ResultSet first, then the Statement
        rs   = Utils.silentClose(rs);
        stmt = Utils.silentClose(stmt);
    }
}
0 голосов
/ 16 ноября 2010

Как метод стоит, он делает все, что угодно

 JDBCTutorialUtilities.printSQLException(e);

происходит, когда происходит исключение, если только этот метод не выбрасывает исключение, вы просто вернетесь из метода, не зная, что это исключение произошло.

Вы можете поместить любой код в блок Exception. Ключевой вопрос - что должен делать вызывающий объект viewTable, если произошло исключение.

Вы, вероятно, имеете код:

viewTable( /*etc*/);

doSomethingWith( price ); // for example

Но это бесполезно, если у вас есть исключение - цена не будет установлена. Так что либо

а). в своем блоке исключений установите флаг, а затем не забудьте проверить его

viewTable( /*etc*/);
if (itAllWorked)
     doSomethingWith( price ); // for example

, который для меня подвержен ошибкам и побеждает весь смысл исключений. или

б). Не перехватывайте исключение в viewTable (за исключением, может быть, его регистрации и повторного выброса, что, как мне кажется, может быть предназначено для служебной программы methid).

try {

       viewTable()
       doSomethingWith(price):
       // all the normal flow
} catch (SqlException e) {
        //some reasnable action, which does not depend on things like proce
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...