Как правильно использовать PreparedStatementCreator Spring JDBC?

согласно моему пониманию, использование PreparedStatement в Java мы можем использовать его несколько раз. Но у меня есть некоторая путаница с использованием PreparedStatementCreator весеннего JDBC.

например, рассмотрим следующий код

public class SpringTest {

    JdbcTemplate jdbcTemplate; 
    PreparedStatementCreator preparedStatementCreator; 
    ResultSetExtractor<String> resultSetExtractor;

    public SpringTest() throws SQLException {

        jdbcTemplate = new JdbcTemplate(OracleUtil.getDataSource());

        preparedStatementCreator = new PreparedStatementCreator() {
            String query = "select NAME from TABLE1  where ID=?";
            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                return connection.prepareStatement(query);
            }
        };

        resultSetExtractor  = new ResultSetExtractor<String>() {
            public String extractData(ResultSet resultSet) throws SQLException,
            DataAccessException {
                if (resultSet.next()) {
                    return resultSet.getString(1);
                }
                return null;
            }
        };
    }
    public String getNameFromId(int id){
        return jdbcTemplate.query(preparedStatementCreator, new Table1Setter(id), resultSetExtractor);
    }

    private static class Table1Setter implements PreparedStatementSetter{

        private int id;
        public Table1Setter(int id) {
            this.id =id;
        }
        @Override
        public void setValues(PreparedStatement preparedStatement) throws SQLException {
            preparedStatement.setInt(1, id);
        }
    }
    public static void main(String[] args) {
        try {
            SpringTest  springTest = new SpringTest();

            for(int i=0;i<10;i++){
                System.out.println(springTest.getNameFromId(i));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

согласно этому коду, когда я вызвал springTest.getNameFromId (int id) метод, он возвращает имя из данного id, здесь я использовал PreparedStatementCreator для создания PreparedStatement и PreparedStatementSetter для установки входных параметров и I получен результат от ResultSetExtractor. Но производительность очень медленная.

после отладки и изучения того, что происходит внутри PreparedStatementCreator и JdbcTemplate, я узнал, что PreparedStatementCreator создает каждый раз новое PreparedStatement...!!!

каждый раз, когда я вызываю метод jdbcTemplate.запрос (preparedStatementCreator, preparedStatementSetter, resultSetExtractor), он создает новое PreparedStatement и это замедляет спектакль.

правильно ли использовать PreparedStatementCreator? Потому что в этом коде я не могу повторно использовать PreparedStatement. И если это правильный способ использовать PreparedStatementCreator, чем как получить выгоду от повторного использования PreparedStatement?

3 ответов


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

поэтому я думаю, что ваше фактическое использование правильно.

jdbctemplate закрывает оператор после его выполнения, поэтому, если вы действительно хотите повторно использовать тот же подготовленный оператор, вы можете прокси-оператор и перехватить метод close в создателе оператора

например (не проверял, только пример):

public abstract class ReusablePreparedStatementCreator implements PreparedStatementCreator {

    private PreparedStatement statement;

    public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
        if (statement != null)
            return statement;

        PreparedStatement ps = doPreparedStatement(conn);

        ProxyFactory pf = new ProxyFactory(ps);
        MethodInterceptor closeMethodInterceptor = new MethodInterceptor() {

            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                return null;  // don't close statement
            }
        };

        NameMatchMethodPointcutAdvisor closeAdvisor = new NameMatchMethodPointcutAdvisor();
        closeAdvisor.setMappedName("close");
        closeAdvisor.setAdvice(closeMethodInterceptor);
        pf.addAdvisor(closeAdvisor);

        statement = (PreparedStatement) pf.getProxy();

        return statement;       
    }

    public abstract PreparedStatement doPreparedStatement(Connection conn) throws SQLException;

    public void close() {
        try {
            PreparedStatement ps = (PreparedStatement) ((Advised) statement).getTargetSource().getTarget();
            ps.close();
        } catch (Exception e) {
            // handle exception
        }
    }

}

вы находитесь на правильном пути использования PreparedStatementCreator.

  1. В каждой новой транзакции, вы должны создать новый PreparedStatement экземпляр, это определенно правильно. PreparedStatementCreator в основном предназначен для обертывания блока кода для создания PreparedStatement экземпляр легко, не говоря, что вы должны повторно использовать новый экземпляр каждый приобретенный товар на аналогичный товар.
  2. PreparedStatement в основном предназначен для отправки шаблонных и предварительно скомпилированных СУБД операторов SQL, которые сэкономят некоторое предварительно скомпилированное время для выполнения SQL.

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


после отладки и изучения того, что происходит внутри PreparedStatementCreator и JdbcTemplate, я узнал, что PreparedStatementCreator создает каждый раз новое PreparedStatement...!!!

Я не уверен, почему это так шокирует, так как это ваш собственный код, который создает новый PreparedStatement каждый раз, называя connection.prepareStatement(query);. Если вы хотите повторно использовать тот же самый, Вы не должны создавать новый.