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

в случае использования 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();

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

Ответы

Ответ 1

Второй способ - более эффективный, но гораздо лучший способ - выполнить их партиями:

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;
            }
        }
    }
}

Ответ 2

Цикл в вашем коде является только упрощенным примером, верно?

Было бы лучше создать PreparedStatement только один раз и повторно использовать его снова и снова в цикле.

В ситуациях, когда это невозможно (поскольку это слишком усложняет поток программы), все же полезно использовать PreparedStatement, даже если вы используете его только один раз, поскольку серверная часть работы (синтаксический анализ SQL и кэширование плана выполнения), будет по-прежнему сокращаться.

Чтобы устранить ситуацию, в которой вы хотите повторно использовать PreparedStatement на стороне Java, некоторые драйверы JDBC (такие как Oracle) имеют функцию кеширования: если вы создаете PreparedStatement для того же SQL в том же соединении, это даст вы тот же (кэшированный) экземпляр.

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