Правильно ли использовать PreparedStatements в потоке в Java? - PullRequest
8 голосов
/ 26 апреля 2010

Я все еще старшекурсник, работаю неполный рабочий день, и поэтому я всегда стараюсь понять, как лучше делать что-то. Недавно мне пришлось написать программу для работы, в которой основной поток программы будет порождать потоки «задачи» (для каждой записи базы данных «задача»), которая будет выполнять некоторые операции, а затем обновлять запись, чтобы сказать, что она завершена. Поэтому мне потребовался объект подключения к базе данных и объекты PreparedStatement в или доступные для объектов ThreadedTask.

Это примерно то, что я в итоге написал, создание PreparedStatement объекта на поток - пустая трата времени? Я думал, что статический PreparedStatments может создать условия гонки ...

Thread A stmt.setInt();
Thread B stmt.setInt();
Thread A stmt.execute();  
Thread B stmt.execute();  

Версия A никогда не исполняется ..

Безопасен ли этот поток? Разве создание и уничтожение PreparedStatement объектов, которые всегда одинаковы, не является огромной тратой?

public class ThreadedTask implements runnable {
    private final PreparedStatement taskCompleteStmt;

    public ThreadedTask() {
        //...
        taskCompleteStmt = Main.db.prepareStatement(...);
    }

    public run() {
        //...
        taskCompleteStmt.executeUpdate();
    }
}

public class Main {
    public static final db = DriverManager.getConnection(...);
}

Ответы [ 3 ]

17 голосов
/ 26 апреля 2010

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

Дайте каждому потоку свое собственное соединение (или синхронизируйте соединение для каждого запроса, но это, вероятно, противоречит цели иметь несколько потоков).

Является ли создание и уничтожение объектов PreparedStatement, которые всегда одинаковы, огромной тратой?

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

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

7 голосов
/ 26 апреля 2010

Безопасность потока здесь не проблема. Все выглядит синтаксически и функционально нормально и должно работать около получаса. Утечка ресурсов, однако, реальная проблема здесь. Приложение рухнет через полчаса, потому что вы никогда не закроете их после использования. База данных, в свою очередь, рано или поздно закроет само соединение, чтобы получить его обратно.

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

public class ThreadedTask implements runnable {
    public run() {
        Connection connection = null;
        Statement statement = null;
        try {
            connection = DriverManager.getConnection(url);
            statement = connection.prepareStatement(sql);
            // ...
        } catch (SQLException e) {
            // Handle?
        } finally {
            if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
            if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
        }
    }
}

Чтобы повысить производительность соединения, используйте пул соединений, например c3p0 (это, кстати, не означает, что вы можете изменить способ написания кода JDBC; всегда получать и закрыть ресурсы в кратчайшей области действия в блоке try-finally).

4 голосов
/ 26 апреля 2010

Лучше всего использовать пул соединений и заставить каждый поток запрашивать соединение из пула. Создайте свои операторы для соединения, которое вам передали, не забывая закрыть его, и, когда вы закончите, верните его обратно в пул. Преимущество использования пула состоит в том, что вы можете легко увеличить количество доступных соединений, если обнаружите, что параллелизм потоков становится проблемой.

...