如何在使用CallableStatementCreator和CallableStatementCallback

时间:2018-06-14 16:15:14

标签: spring connection database-connection jdbctemplate callable-statement

如何在以下代码段中关闭CallableStatement。 Sonarcube抱怨我要关闭CallableStatement

我在doInCallableStatement方法中添加了finally块并关闭了callableStatement。它完美地工作并且callableStatement正在关闭,但仍然是Sonarcube抱怨 我要关闭callableStatement。

如果我在createCallableStatement方法中添加finally块并关闭callableStatement。 Sonarcube没有抱怨,但CallableStatementCallback失败了,因为callableStatement已经关闭。

@Override
public MyInfo getMyInfo(String id) throws SecurityDAOException, AuditException {
    MyInfo myInfo = null;
    try {
        myInfo = jdbcTemplate.execute(new CallableStatementCreator() {
            @Override
            public CallableStatement createCallableStatement(Connection connection) throws SQLException {
                CallableStatement callableStatement = null;
                callableStatement = connection.prepareCall(STORED_PROCEDURE);
                callableStatement.setString("ID", Id);
                callableStatement.registerOutParameter("REQID", OracleTypes.VARCHAR);
                callableStatement.registerOutParameter("TYPE", OracleTypes.VARCHAR);
                return callableStatement;

                // If I add finally block here and close callableStatement, 
                // Sonarcube is not complaining but CallableStatementCallback 
                // is failing since the callableStatement is already closed.
            }
        }, new CallableStatementCallback<MyInfo>() {
            @Override
            public MyInfo doInCallableStatement(CallableStatement callableStatement) throws SQLException {
                try {
                    MyInfo myInfo = new MyInfo();
                    callableStatement.execute();
                    myInfo.setSsn(callableStatement.getString("REQID"));
                    myInfo.setSsnType(String.valueOf(callableStatement.getString("TYPE").charAt(0)));
                    return myInfo;
                } finally {
                    if (null != callableStatement) {
                        try {
                            callableStatement.close();
                        } catch (SQLException e) {
                            LOGGER.error("Not able to close CallableStatement:", e);
                        }
                    }
                }
            }
        });
    } catch (DataAccessException dataAccessException) {
        throw new SecurityDAOException(dataAccessException, INTERNAL_SERVER_ERROR,
                RESPONSE_CODE_INTERNAL_SERVER_ERROR, dataAccessException.getCause().getMessage());
    } catch (Exception e) {
        throw new AuditException(e);
    }
    return myInfo;
}

更新 - (根据评论更新代码以使其正常工作)

@Vasan,我试过你的方法,但出于某种原因,我无法使其发挥作用。我得到以下异常 "org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{call MY_PROCEDURE(?,?)}]; SQL state [99999]; error code [17090]; operation not allowed: Ordinal binding and Named binding cannot be combined!; nested exception is java.sql.SQLException: operation not allowed: Ordinal binding and Named binding cannot be combined!"

虽然我没有结合Ordinal绑定和命名绑定。任何帮助表示赞赏。

@Override
public String getMyInfo(String id) throws SecurityDAOException, AuditException {
    String status = null;
    try {
        CallableStatementCreatorFactory cscFactory = new CallableStatementCreatorFactory(
                MY_PROCEDURE);
        cscFactory.addParameter(new SqlParameter("ID", OracleTypes.VARCHAR));
        cscFactory.addParameter(new SqlOutParameter("MY_OUTPUT", OracleTypes.VARCHAR));

        final Map<String, Object> actualParams = new HashMap<>();
        actualParams.put("ID", id);

        CallableStatementCreator callableStatementCreator = cscFactory.newCallableStatementCreator(actualParams);

        CallableStatementCallback<String> callableStatementCallback = new CallableStatementCallback<String>() {
            @Override
            public String doInCallableStatement(CallableStatement callableStatement) throws SQLException {
                String status = null;
                callableStatement.execute();
                status = callableStatement.getString(2);
                return status;
            }
        };

        status = jdbcTemplate.execute(callableStatementCreator, callableStatementCallback);

    } catch (DataAccessException dataAccessException) {
        throw new SecurityDAOException(dataAccessException, INTERNAL_SERVER_ERROR,
                RESPONSE_CODE_INTERNAL_SERVER_ERROR, dataAccessException.getCause().getMessage());
    } catch (Exception e) {
        throw new AuditException(e);
    }
    return status;
}

1 个答案:

答案 0 :(得分:1)

使用您当前的方法,似乎无法避免SONAR问题。事实上,Spring自己关闭了语句,所以你甚至不应该关闭CallableStatementCallback impl类中的语句,更不用说在CallableStatementCreator impl类中。

Quote from javadocs

  

Spring将在回调返回后关闭Statement对象

有一种替代方法可以创建CallableStatementCreator,而不是创建自己的impl类,这可以帮助您避免SONAR问题。您可以使用CallableStatementCreatorFactory

CallableStatementCreatorFactory cscFactory = new CallableStatementCreatorFactory(STORED_PROCEDURE);
cscFactory.addParameter(new SqlParameter("ID", OracleTypes.VARCHAR));
cscFactory.addParameter(new SqlOutParameter("REQID", OracleTypes.VARCHAR));
cscFactory.addParameter(new SqlOutParameter("TYPE", OracleTypes.VARCHAR));

CallableStatementCreator cscCreator  = cscFactory.newCallableStatementCreator(Collections.singletonMap("ID", id));
jdbcTemplate.execute(cscCreator, [...])

我之前没有使用过这个,这只是使用javadocs编写的。因此,可能需要对您的需求进行一些小的调整。