Повторне використання PreparedStatement кілька разів


96

у випадку використання PreparedStatement з одним загальним підключенням без будь-якого пулу, чи можу я відтворити екземпляр для кожної операції dml / sql, що зберігає силу підготовлених операторів?

Я маю на увазі:

for (int i=0; i<1000; i++) {
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setObject(1, someValue);
    preparedStatement.executeQuery();
    preparedStatement.close();
}

замість:

PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i=0; i<1000; i++) {
    preparedStatement.clearParameters();
    preparedStatement.setObject(1, someValue);
    preparedStatement.executeQuery();
}
preparedStatement.close();

моє запитання виникає через те, що я хочу розмістити цей код у багатопотоковому середовищі, чи можете ви дати мені якусь пораду? Дякую


отже, ваш запит sqlне змінюється в циклі? якщо цей запит не змінюється для кожної ітерації циклу, то чому ви створюєте новий PreparedStatementдля кожної ітерації (у першому фрагменті коду)? Чи є для цього причина?
Сабір Хан,

скажімо, якщо запит змінюється, то все-таки другий підхід краще, чи не так? Якісь недоліки?
Stunner

Відповіді:


144

Другий спосіб - трохи ефективніший, але набагато кращий спосіб - виконувати їх партіями:

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL);
    ) {
        for (Entity entity : entities) {
            statement.setObject(1, entity.getSomeProperty());
            // ...

            statement.addBatch();
        }

        statement.executeBatch();
    }
}

Однак ви залежать від реалізації драйвера JDBC, скільки пакетів ви можете виконати одночасно. Наприклад, ви можете виконати їх кожні 1000 партій:

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL);
    ) {
        int i = 0;

        for (Entity entity : entities) {
            statement.setObject(1, entity.getSomeProperty());
            // ...

            statement.addBatch();
            i++;

            if (i % 1000 == 0 || i == entities.size()) {
                statement.executeBatch(); // Execute every 1000 items.
            }
        }
    }
}

Що стосується багатопоточних середовищ, вам не потрібно турбуватися про це, якщо ви набуваєте та закриваєте з'єднання та оператор у найкоротшій можливості в межах того самого блоку методу відповідно до звичайної ідіоми JDBC, використовуючи оператор try-with-resources, як показано в вище фрагменти.

Якщо ці пакети є транзакційними, тоді ви хочете вимкнути автозв'язок з'єднання і здійснити транзакцію лише тоді, коли всі пакети закінчені. Інакше це може призвести до брудної бази даних, коли перша група пакетів вдалася, а пізніше ні.

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (Connection connection = dataSource.getConnection()) {
        connection.setAutoCommit(false);

        try (PreparedStatement statement = connection.prepareStatement(SQL)) {
            // ...

            try {
                connection.commit();
            } catch (SQLException e) {
                connection.rollback();
                throw e;
            }
        }
    }
}

inside the same method block- ви маєте на увазі, що кожен потік матиме власний стек, і ці зв’язки та оператори знаходяться в стеку з одного боку, а з іншого джерела даних даватимуть кожному новому виклику executeFunction (== кожен потік) окремий екземпляр підключення. Я вас добре розумію? "
Pavel_K

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

Ваша відповідь хороша за умови, що запит не змінюється у циклі .. що, якщо запит змінюється, наприклад, у моєму випадку, коли запит змінюється .. я припускаю, що все-таки другий підхід кращий. будь ласка, підтвердьте
Stunner

13

Цикл у вашому коді - це лише надто спрощений приклад, чи не так?

Було б краще створити PreparedStatementєдиний раз і повторно використовувати його знову і знову в циклі.

У ситуаціях, коли це неможливо (оскільки це занадто ускладнило потік програми), все одно вигідно використовувати a PreparedStatement, навіть якщо ви використовуєте його лише один раз, оскільки робота на стороні сервера (аналіз SQL та кешування виконання плану), як і раніше буде зменшено.

Для вирішення ситуації, коли ви хочете повторно використовувати сторону Java PreparedStatement, деякі драйвери JDBC (наприклад, Oracle) мають функцію кешування: Якщо ви створюєте PreparedStatementдля одного і того ж SQL на одному підключенні, він надасть вам те саме (кешоване) ) екземпляр.

Про багатопотоковість: Я не думаю, що з'єднання JDBC можуть бути спільно використані між кількома потоками (тобто використовуються одночасно кількома потоками). Кожен потік повинен отримати власне з'єднання з пулу, використати його і повернути до пулу знову.


1
Насправді у з'єднання є свій ексклюзивний потік, і кожен оператор виконується в ньому, але я отримую доступ через відкритий стек підготовлених операторів до цього потоку. Отже, інші паралельні потоки спочатку передають лише параметри, необхідні для побудови всього підготовленого оператора, але потім вони можуть одночасно змінювати параметри
Steel Plume,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.