нулевая проверка в попытке с ресурсами

У меня есть следующий код:

try (Connection connection = getConnection();

    PreparedStatement preparedStatement = connection.prepareStatement(someSql)) {//stuff}

Как мне проверить, что соединение здесь не является нулевым?

Кроме того, у меня есть метод, который возвращает PreparedStatement следующим образом:

private PreparedStatement getPreparedStatement(Connection connection)

  throws SQLException {

PreparedStatement preparedStatement = connection.prepareStatement(SQL);

preparedStatement.setString(1, "someString");

return preparedStatement;

}

это вызывается в методе, который использует следующую попытку с ресурсами:

try (Connection connection = getConnection();
    PreparedStatement preparedStatement =
        getPreparedStatement(connection)) {//stuff}

Теперь я бы предположил, что подготовленный оператор будет автоматически закрыт, потому что он инициируется при попытке с ресурсами. Но SonarCloud говорит, что я должен использовать try с ресурсами в методе getPreparedStatement или закрыть этот PreparedStatement в блоке finally. Является ли это неправильным выводом SonarCloud или есть лучший способ сделать это?


person uhmdunnolol    schedule 28.02.2020    source источник
comment
Так это два разных вопроса? Как в первом блоке кода вопрос отличается от остальных?   -  person Nexevis    schedule 28.02.2020
comment
Определенно неверный вывод SonarCloud. И это далеко не первое. Инструменты анализа кода сложно написать.   -  person VGR    schedule 28.02.2020


Ответы (2)


getConnection должен вызывать исключение вместо возврата null. Возвращать null нехорошо. SonarCloud seems to be wanting you to put a try-with-resource opening ingetPreparedStatement(must include thesetString`) и закрывать в вызывающем методе, что, конечно, вы не можете сделать как таковое.

Лучшим подходом является идиома Execute Around. Вместо getPreparedStatement возвращает проход PreparedStatement в лямбда-выражении (обычно) для выполнения. Затем ресурс можно аккуратно закрыть в операторе try-with-resource.

/*** NICE ***/
// Function instead of Consumer would allow the method to return a value.
private void prepared(
    Connection connection, Consumer<PreparedStatement> op
) throws SQLException {
    // Might want to add getConnection in here too, perhaps.
    try (
        PreparedStatement statement =
             connection.prepareStatement(SQL)
    ) { 
        statement.setString(1, "someString");
        op.accept(statement);
    }
}

Используется как:

    try (Connection connection = getConnection()) {
        prepared(connection, statement -> {
            // blah, blah, blah
        });
    }

Хакерская альтернатива состоит в том, чтобы включить оператор try в getPreparedStatement, который закрывается только в условиях ошибки.

/*** HACKY ***/
private PreparedStatement prepared(
    Connection connection
) throws SQLException {
    boolean success = false;
    PreparedStatement statement =
        connection.prepareStatement(SQL);
    try { 
        statement.setString(1, "someString");
        success = true;
        return preparedStatement;
    } finally {
        if (!success) {
            statement.close();
        }
    }
}

Если вы отчаянно хотите, чтобы getConnection вернуло null (не надо) и использовали одну попытку с ресурсом с двумя входами, то условный оператор работает.

try (
    Connection connection = getConnection();
    PreparedStatement statement =
        connection==null ? null : connection.prepareStatement(SQL)
) {
person Tom Hawtin - tackline    schedule 28.02.2020

Самый простой подход и, по-моему, наиболее читаемый вариант — использовать 2 блока try-with-resources. Даже сонар имеет встроенное исключение для запрета вложенных блоков try-catch именно для этого случая использования...

try (Connection conn = getConnection()) {
    if (conn != null) {
        try (PreparedStatement stmt = ...) {
            // do stuff
        }
    }
}

Если do stuff длинный, его можно и нужно реорганизовать в отдельный метод (возможно, включая внутреннюю попытку с ресурсами).

person Nicktar    schedule 28.02.2020
comment
это то, что я тоже придумал, но действительно ли это хорошая практика? - person uhmdunnolol; 28.02.2020
comment
Я видел и использовал его в нескольких проектах и ​​компаниях. Я видел и использовал его во многих обзорах кода без жалоб, и даже если даже у сонара есть специальное правило, разрешающее это (обычно оно не одобряет вложенные блоки try-catch), я думаю, что это по крайней мере достаточно хороший подход для меня, пока я не увижу лучше один. Одним из улучшений было бы предотвращение возврата getConnection значения null. - person Nicktar; 28.02.2020
comment
getConnection должен никогда возвращать значение null. - person VGR; 28.02.2020