Безопасно ли использовать статический экземпляр java.sql.Connection в многопоточной системе?
Я запускаю веб-приложение на Tomcat. У меня есть класс, который обрабатывает все запросы к БД. Этот класс содержит Connection
объект и методы, которые возвращают результаты запроса.
Это объект connection:
private static Connection conn = null;
У него только один экземпляр (singleton).
Кроме того, у меня есть методы, которые выполняют запросы, такие как поиск пользователя в БД:
public static ResultSet searchUser(String user, String pass) throws SQLException
Этот метод использует статический Connection
объект. Мой вопрос в том, безопасно ли мое использование в потоках статических Connection
объектов? Или это может вызвать проблемы, когда многие пользователи будут вызывать метод searchUser
?
Переведено автоматически
Ответ 1
является ли мое использование в статическом объекте Connection потокобезопасным?
Категорически нет!
Таким образом, соединение будет общим для всех запросов, отправляемых всеми пользователями, и, следовательно, все запросы будут мешать друг другу. Но безопасность потоков - не единственная ваша проблема, утечка ресурсов - также другая ваша проблема. Вы поддерживаете одно соединение открытым в течение всего срока службы приложения. Средняя база данных восстанавливает соединение всякий раз, когда оно было открыто слишком долго, что обычно составляет от 30 минут до 8 часов, в зависимости от конфигурации базы данных. Итак, если ваше веб-приложение работает дольше указанного срока, соединение будет потеряно, и вы больше не сможете выполнять запросы.
Эта проблема также возникает, когда эти ресурсы хранятся как не-static
переменная экземпляра экземпляра класса, которая используется повторно несколько раз.
Вы должны всегда получать и закрывать connection, оператор и результирующий набор в минимально возможной области, предпочтительно внутри того же try-with-resources
блока, где вы выполняете запрос, в соответствии со следующей идиомой JDBC:
public User find(String username, String password) throws SQLException {
User user = null;
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT id, username, email FROM user WHERE username=? AND password=md5(?)");
) {
statement.setString(1, username);
statement.setString(2, password);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
user = new User();
user.setId(resultSet.getLong("id"));
user.setUsername(resultSet.getString("username"));
user.setEmail(resultSet.getString("email"));
}
}
}
return user;
}
Обратите внимание, что вы должны не возвращать ResultSet
здесь. Вы должны немедленно прочитать его и сопоставить с классом, отличным от JDBC, а затем вернуть его, чтобы ResultSet
можно было безопасно закрыть.
Если вы еще не на Java 7, то используйте try-finally
блок, в котором вы вручную закрываете доступные ресурсы в порядке, обратном тому, в каком вы их приобрели. Вы можете найти пример здесь: Как часто следует закрывать Connection, Statement и ResultSet в JDBC?
Если вы беспокоитесь о производительности подключения, то вместо этого вам следует использовать пул подключений. Это встроено во многие серверы приложений Java EE, и даже простые сервлетконтейнеры, такие как Tomcat, поддерживают это. Просто создайте источник данных JNDI на самом сервере и позвольте вашему веб-приложению использовать его как DataSource
. Очевидно, что это уже пул подключений. Вы можете найти пример по первой ссылке из списка ниже.
Смотрите также:
- Как я должен подключиться к базе данных / источнику данных JDBC в приложении на основе сервлетов?
- Когда мое приложение теряет соединение, как я должен его восстановить?
- Использую ли я пул соединений JDBC?
- Показать результирующий набор JDBC в формате HTML на странице JSP с использованием шаблона MVC и DAO
- Руководство по DAO с JDBC
Ответ 2
Если вы выполняете только Select
запросы (searchUser
звучит как только выбор данных), проблем не возникнет, кроме конфликта потоков.
Насколько я знаю, a Connection
может обрабатывать только один запрос одновременно, поэтому, используя один экземпляр, вы, по сути, сериализуете доступ к базе данных. Но это не обязательно означает, что всегда безопасно обращаться к такой базе данных в многопоточной среде. Все еще могут возникнуть проблемы, если параллельные обращения чередуются.