Использование подготовленных операторов с JDBCTemplate

Я использую шаблон JDBC и хочу читать из базы данных, используя подготовленные операторы. Я перебираю множество строк в файле .csv и в каждой строке выполняю несколько запросов на выборку SQL с соответствующими значениями.

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

Существует PreparedStatementCreator и PreparedStatementSetter. Как и в этом примере, оба они созданы с анонимными внутренними классами. Но внутри класса PreparedStatementSetter у меня нет доступа к значениям, которые я хочу установить в подготовленном операторе.

Поскольку я просматриваю файл .csv, я не могу жестко закодировать их как String, потому что я их не знаю. Я также не могу передать их PreparedStatementSetter, потому что для конструктора нет аргументов. И установить мои значения на final тоже было бы глупо.

Я привык к тому, что создание заранее подготовленных заявлений было довольно простым. Что-то типа

PreparedStatement updateSales = con.prepareStatement(
    "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
updateSales.setInt(1, 75); 
updateSales.setString(2, "Colombian"); 
updateSales.executeUpdate():

как в этом руководстве по Java.


person Community    schedule 07.06.2010    source источник


Ответы (5)


По умолчанию JDBCTemplate выполняет свои собственные PreparedStatement внутри, если вы просто используете форму .update(String sql, Object ... args). Spring и ваша база данных будут управлять скомпилированным запросом за вас, поэтому вам не нужно беспокоиться об открытии, закрытии, защите ресурсов и т. Д. Одно из спасительных достоинств Spring. Ссылка на документацию Spring 2.5 по этому поводу. Надеюсь, что это поможет вещи яснее. Кроме того, кэширование операторов может выполняться на уровне JDBC, как в случае по крайней мере, некоторые из драйверов Oracle JDBC. Здесь будет гораздо больше деталей, чем я могу компетентно.

person mezmo    schedule 07.06.2010
comment
Но я хочу выполнить выбор в базе данных, а не обновление. В справочнике Spring static. springsource.org/spring/docs/2.0.x/api/org/ написано, что с update могут выполняться только вставка, обновление или удаление. - person ; 08.06.2010
comment
@ user3211068 есть query метод для выбора - person linqu; 06.03.2014
comment
@mezmo не могли бы вы добавить источник для своего утверждения? И то же самое верно для запроса (String sql, ...)? - person leo; 28.05.2014
comment
В исходном коде JdbcTemplate просто создает новый PreparedStatement каждый раз, когда я вызываю query("select..."). Означает ли это, что драйвер JDBC отвечает за повторное использование скомпилированных операторов? - person Bastian Voigt; 30.10.2014
comment
Исходя из моего собственного опыта, в основном с Oracle и DB2, сама база данных будет кэшировать подготовленный оператор в некоторых случаях, и, по крайней мере, согласно документации Oracle, драйверы JDBC имеют свой собственный кеш операторов и будут перемещать оператор в cache при вызове оператора close. Я добавлю эту ссылку к своему исходному ответу .... и спасибо за редактирование, Бастиан. - person mezmo; 30.10.2014
comment
NamedParameterJdbcTemplate делает то же самое? - person Fund Monica's Lawsuit; 01.02.2019
comment
Согласно документам, docs.spring.io/spring-framework/docs/current/javadoc-api/org/, да. - person mezmo; 01.02.2019

Попробуйте следующее:

PreparedStatementCreator creator = new PreparedStatementCreator() {
    @Override
    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
        PreparedStatement updateSales = con.prepareStatement(
        "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
        updateSales.setInt(1, 75); 
        updateSales.setString(2, "Colombian"); 
        return updateSales;
    }
};
person Kevin    schedule 07.06.2010
comment
Это сработает, но значения, которые я хочу установить, находятся за пределами внутреннего анонимного класса. Внутри класса должно быть что-то вроде updateSales.setString(2, fileRow.getName()), но я не могу получить доступ к fileRow форме внутри класса. - person ; 07.06.2010

Я бы выделил подготовленную обработку оператора как минимум в метод. В этом случае, поскольку результатов нет, это довольно просто (и при условии, что соединение является переменной экземпляра, которая не изменяется):

private PreparedStatement updateSales;
public void updateSales(int sales, String cof_name) throws SQLException {
    if (updateSales == null) {
        updateSales = con.prepareStatement(
            "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");
    }
    updateSales.setInt(1, sales);
    updateSales.setString(2, cof_name);
    updateSales.executeUpdate();
}

На этом этапе остается лишь позвонить:

updateSales(75, "Colombian");

Что довольно просто интегрировать с другими вещами, да? И если вы вызовете метод много раз, обновление будет создано только один раз, что значительно ускорит работу. Ну, если вы не делаете сумасшедших вещей вроде выполнения каждого обновления в отдельной транзакции ...

Обратите внимание, что типы фиксированы. Это связано с тем, что для любого конкретного запроса / обновления они должны быть исправлены, чтобы база данных могла эффективно выполнять свою работу. Если вы просто извлекаете произвольные строки из файла CSV, передайте их как строки. Также нет блокировки; Намного лучше вместо этого использовать отдельные соединения из одного потока.

person Donal Fellows    schedule 07.06.2010
comment
Для запросов, возвращающих одно значение, эту технику довольно просто использовать. Основная сложность возникает, когда у вас есть запросы, возвращающие много значений; либо вернуть ResultSet, либо передать обратный вызов, который будет обрабатывать каждую возвращаемую строку (конечно, со значениями, выделенными из ResultSet). - person Donal Fellows; 07.06.2010
comment
Извините, но я не знаю, какое отношение это имеет к моей проблеме с шаблоном jdbc. Я не могу передать запрос шаблона jdbc с помощью PreparedStatement. Похоже, мне нужен PreparedStatementCreator или PreparedStatementSetter. - person ; 07.06.2010

Я пробовал оператор выбора сейчас с помощью PreparedStatement, но оказалось, что не быстрее, чем шаблон Jdbc. Возможно, как предложил мезмо, он автоматически создает подготовленные операторы.

Во всяком случае, причина того, что мой sql SELECTs был таким медленным, была другая. В предложении WHERE я всегда использовал оператор LIKE, когда все, что мне нужно было сделать, это найти точное совпадение. Как я выяснил, LIKE ищет шаблон и поэтому работает довольно медленно.

Сейчас я использую оператор =, он намного быстрее.

person Community    schedule 08.06.2010
comment
Шаблоны Spring JDBC открывают и закрывают операторы при каждом вызове, поэтому каждый вызов имеет все накладные расходы, связанные с подготовкой + выполнением. Подготовленный оператор дает преимущество в производительности только в том случае, если вы берете под свой контроль жизненный цикл оператора. По-прежнему есть преимущество в плане безопасности, поскольку SQL не нужно дезинфицировать. - person pojo-guy; 27.02.2019

person    schedule
comment
часть запроса как лямбда-стиль java 8: jdbcTemplate.query(sql, ps -> ps.setString(1, "value"), (rs, i) -> rs.getLong(1) - person Stian Storrvik; 12.05.2017