Повторное использование PreparedStatement несколько раз - PullRequest
95 голосов
/ 18 марта 2010

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

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

Ответы [ 2 ]

139 голосов
/ 18 марта 2010

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

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;
            }
        }
    }
}
12 голосов
/ 18 марта 2010

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

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

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

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

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

...