Можно ли использовать статический экземпляр java.sql.Connection в многопоточной системе?

Я запускаю веб-приложение на Tomcat. У меня есть класс, который обрабатывает все запросы БД. Этот класс содержит объект Connection и методы, которые возвращают результаты запроса.

Это объект соединения:

private static Connection conn = null;

Он имеет только один экземпляр (singleton).

Кроме того, у меня есть методы, которые выполняют запросы, такие как поиск пользователя в db:

public static ResultSet searchUser(String user, String pass) throws SQLException

Этот метод использует статический объект Connection. Мой вопрос в том, является ли мое использование в статическом потоке объектов Connection безопасным? Или это может вызвать проблемы, когда многие пользователи вызовут метод searchUser?

Ответ 1

мое использование в статическом потоке объекта Connection безопасно?

Точно нет!

Таким образом, соединение будет общим для всех запросов, отправленных всеми пользователями, и, таким образом, все запросы будут мешать друг другу. Но потокобезопасность - не единственная ваша проблема, утечка ресурсов - это и другая ваша проблема. Вы сохраняете одно соединение открытым в течение всего срока службы приложения. Средняя база данных будет восстанавливать соединение всякий раз, когда оно было открыто слишком долго, что обычно составляет от 30 минут до 8 часов, в зависимости от конфигурации БД. Поэтому, если ваше веб-приложение будет работать дольше, соединение будет потеряно, и вы больше не сможете выполнять запросы.

Эта проблема также применима, когда эти ресурсы хранятся в виде static переменной экземпляра non- экземпляра класса, который используется многократно.

Вы всегда должны получать и закрывать соединение, оператор и набор результатов в кратчайшей возможной области, предпочтительно внутри того же блока 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 здесь. Вы должны немедленно прочитать его и сопоставить его с классом non- JDBC, а затем вернуть его, чтобы ResultSet можно было безопасно закрыть.

Если вы еще не работаете в Java 7, используйте блок try-finally котором вы вручную закрываете закрываемые ресурсы в обратном порядке по мере их приобретения. Вы можете найти пример здесь: как часто должны быть закрыты Connection, Statement и ResultSet в JDBC?

Если вы беспокоитесь о производительности подключения, то вам следует использовать вместо этого пул подключений. Это встроено во многие серверы приложений Java EE и даже в базовые контейнеры сервлетов, такие как Tomcat. Просто создайте источник данных JNDI на самом сервере и позвольте вашему веб-приложению захватить его как DataSource. Это прозрачно уже пул соединений. Вы можете найти пример в первой ссылке списка ниже.

Смотрите также:

Ответ 2

Если вы работаете только с запросами Select (searchUser звучит, как только выбор данных), не будет проблем, кроме конфликтов потоков.

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