Практика объединения соединений JDBC MySql во избежание исчерпания пула соединений

У меня есть веб-приложение Java-JSF на GlassFish, в котором я хочу использовать пул соединений. Поэтому я создал application scoped bean, который обслуживает экземпляры Connection для других beans:

public class DatabaseBean {

    private DataSource myDataSource;

    public DatabaseBean() {
        try {
            Context ctx = new InitialContext();
            ecwinsDataSource = (DataSource) ctx.lookup("jdbc/myDataSource");
        } catch (NamingException ex) {
            ex.printStackTrace();
        }
    }

    public Connection getConnection() throws ClassNotFoundException, SQLException, InstantiationException, IllegalAccessException {
        Connection connection = myDataSource.getConnection();
        System.out.println("Succesfully connected: " + connection);
        //Sample: Succesfully connected: [email protected]
        return connection;
    }
}

Таким образом, пул соединений заполняется очень быстро; после нескольких переходов через "связанные с db" представления приложение останавливается со следующим:

RAR5117: Не удалось получить/создать соединение из пула соединений [mysql_testPool]. Причина. Используемые соединения равны максимальному размеру пула и истекшему максимальному времени ожидания. Невозможно выделить больше соединений. RAR5114: Ошибка выделения соединения: [Ошибка при распределении соединения. Причина. Используемые соединения равны максимальному размеру пула и истечению максимального времени ожидания. Невозможно выделить больше соединений.] Java.sql.SQLException: ошибка при распределении соединения. Причина. Используемые соединения равны максимальному размеру пула и истечению максимального времени ожидания. Невозможно выделить больше соединений.

Я закрываю соединения и другие ресурсы в каждом методе. Приложение работает со всеми автономными соединениями.

Что я делаю неправильно? Любые советы или рекомендации будут оценены.

Ответ 1

Исключение указывает типичный случай кода приложения, который теряет соединения с базой данных. Вы должны убедиться, что вы приобрели и все (Connection, Statement и ResultSet) в try-with-resources блок в том же блоке метода в соответствии с нормальной идиомой JDBC.

public void create(Entity entity) throws SQLException {
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL_CREATE);
    ) { 
        statement.setSomeObject(1, entity.getSomeProperty());
        // ...
        statement.executeUpdate();
    }
}

Или, если вы не на Java 7, в блоке try-finally. Закрытие их в finally гарантирует, что они также будут закрыты в случае исключений.

public void create(Entity entity) throws SQLException {
    Connection connection = null;
    PreparedStatement statement = null;

    try { 
        connection = dataSource.getConnection();
        statement = connection.prepareStatement(SQL_CREATE);
        statement.setSomeObject(1, entity.getSomeProperty());
        // ...
        statement.executeUpdate();
    } finally {
        if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
        if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
    }
}

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

public void close() throws SQLException {
    if (this.connection is still eligible for reuse) {
        do not close this.connection, but just return it to pool for reuse;
    } else {
        actually invoke this.connection.close();
    }
}

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

См. также:

Ответ 2

Если вам нужен пул соединений JDBC, почему бы вам не полагаться на то, что уже доступно? AFAIK, объединение JDBC-соединений считается более или менее стандартной функцией на этих серверах приложений Java, и IMO, вы не должны создавать это самостоятельно, если вас просто интересует создание приложения.

Здесь ссылка, с которой вы должны начать: http://weblogs.java.net/blog/2007/09/12/totd-9-using-jdbc-connection-pooljndi-name-glassfish-rails-application

Что вы, вероятно, должны делать, это выяснить, как разрешить вашему приложению захватить соединение из пула с помощью jndi.