Java 连接池无法正常工作

问题描述 投票:0回答:1

我正在使用 Tomcat 构建一个 Web 项目,我必须从 MySQL 数据库中获取一些日期,为此我使用 HikariCP。 不幸的是,在多次获取之后,我得到了 SQLTransientConnectionException

13:34:33.915 [http-nio-8081-exec-14] ERROR de.intranet.data.db.DBAccess - SQL error in getLatestModification for category hausnachrichten: Cannot invoke "java.sql.Connection.prepareStatement(String)" because the return value of "de.intranet.data.db.DBAccess.getConnection()" is null
13:34:33.915 [http-nio-8081-exec-14] DEBUG de.intranet.data.db.DBAccess - Existing getLatestModification for category hausnachrichten: null
13:34:38.957 [http-nio-8081-exec-13] ERROR de.intranet.data.db.DBConnection - Failed to retrieve a connection from the connection pool.
java.sql.SQLTransientConnectionException: HikariPool-2 - Connection is not available, request timed out after 30010ms.
   at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:696) ~[HikariCP-5.0.1.jar:?]
   at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:181) ~[HikariCP-5.0.1.jar:?]
   at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:146) ~[HikariCP-5.0.1.jar:?]
   at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:100) ~[HikariCP-5.0.1.jar:?]
   at de.intranet.data.db.DBConnection.getConnection(DBConnection.java:131) [classes/:?]
   at de.intranet.data.db.DBAccess.getLatestModification(DBAccess.java:735) [classes/:?]
   at de.intranet.http.ServletDashboardPrepopulate.doPost(ServletDashboardPrepopulate.java:80) [classes/:?]
   at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) [servlet-api.jar:6.0]
   at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) [servlet-api.jar:6.0]
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) [catalina.jar:10.1.10]
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) [catalina.jar:10.1.10]
   at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) [tomcat-websocket.jar:10.1.10]
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) [catalina.jar:10.1.10]
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) [catalina.jar:10.1.10]
   at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166) [catalina.jar:10.1.10]
   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [catalina.jar:10.1.10]
   at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) [catalina.jar:10.1.10]
   at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) [catalina.jar:10.1.10]
   at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [catalina.jar:10.1.10]
   at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:676) [catalina.jar:10.1.10]
   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [catalina.jar:10.1.10]
   at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341) [catalina.jar:10.1.10]
   at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) [tomcat-coyote.jar:10.1.10]
   at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-coyote.jar:10.1.10]
   at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894) [tomcat-coyote.jar:10.1.10]
   at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741) [tomcat-coyote.jar:10.1.10]
   at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-coyote.jar:10.1.10]
   at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-util.jar:10.1.10]
   at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-util.jar:10.1.10]
   at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:10.1.10]
   at java.base/java.lang.Thread.run(Thread.java:1589) [?:?]

对我来说,就我正在使用的

try-with-statements
而言,连接似乎已正确关闭,突然出现SQLException

13:33:28.868 [http-nio-8081-exec-11] DEBUG de.intranet.http.ServletDashboardPrepopulate - Connection Pool Stats - Active: 10, Idle: null
13:33:33.903 [http-nio-8081-exec-14] DEBUG de.intranet.http.ServletDashboardPrepopulate - Connection Pool Stats - Active: 10, Idle: null
13:33:38.934 [http-nio-8081-exec-13] DEBUG de.intranet.http.ServletDashboardPrepopulate - Connection Pool Stats - Active: 10, Idle: null
13:33:43.960 [http-nio-8081-exec-2] DEBUG de.intranet.http.ServletDashboardPrepopulate - Connection Pool Stats - Active: 10, Idle: null
13:33:48.995 [http-nio-8081-exec-16] DEBUG de.intranet.http.ServletDashboardPrepopulate - Connection Pool Stats - Active: 10, Idle: null
13:33:53.856 [http-nio-8081-exec-9] ERROR de.intranet.data.db.DBConnection - Failed to retrieve a connection from the connection pool.
java.sql.SQLTransientConnectionException: HikariPool-2 - Connection is not available, request timed out after 30001ms.
    at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:696) ~[HikariCP-5.0.1.jar:?]
(rest is same as above)
 @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            // Initialize a map to store category names and their corresponding latest
            // modification dates
            Map<String, String> dateMap = new HashMap<>();

            // Retrieve the ServletContext and DBAccess instance from the request
            ServletContext servletContext = request.getServletContext();
            DBAccess dbAccess = (DBAccess) servletContext.getAttribute("dbConnection");
            // Define a date format for formatting the modification dates in the response
            SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");

            try {
                Map<String, Integer> poolStats = dbAccess.getConnectionPoolStats();
                logger.debug("Connection Pool Stats - Active: {}, Idle: {}", poolStats.get("active"),
                        poolStats.get("idle"));
            } catch (Exception e) {
                logger.error("Error retrieving connection pool stats: {}", e.getMessage());
            }

            // Iterate through each file category and retrieve the latest modification date
            for (DBFilesTableCategory category : DBFilesTableCategory.values()) {
                Date lastDate = dbAccess.getLatestModification(category);
                // If a modification date is available, add it to the dateMap
                if (lastDate != null) {
                    dateMap.put(category.name(), sdf.format(lastDate));
                } else {
                    dateMap.put(category.name(), "Keine Dateien vorhanden");
                }
            }
            // Convert the dateMap to a JSON response using Gson
            Gson gson = new Gson();
            String jsonResponse = gson.toJson(dateMap);
            // Write the JSON response to the HttpServletResponse
            response.getWriter().write(jsonResponse);

        } catch (Exception e) {
            // Log any exceptions that occur during the processing of the request
            logger.error("Error while prepopulating dashboard: {}", e.getMessage());

            // Send an internal server error response to the client
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
public class DBAccess extends DBConnection {
public Date getLatestModification(DBFilesTableCategory category) {
        // Variable to store the latest modification date
        Date latestDate = null;
        // Convert the category enum to a string
        String categoryString = category.toString();
        // SQL query to retrieve the maximum upload date for files in the specified
        // category
        String query = "SELECT MAX(f_uploadDate) AS latestDate FROM files WHERE f_category = ?";
        try (Connection connection = this.getConnection();
                PreparedStatement preparedStatement = this.getConnection().prepareStatement(query)) {

            // Set the category as a parameter in the prepared statement
            preparedStatement.setString(1, categoryString);
            logger.debug("Executing query: {}", preparedStatement);
            // Execute the query and retrieve the result set
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                // If a row is returned, retrieve the latest date
                if (resultSet.next()) {
                    latestDate = resultSet.getDate("latestDate");
                }
            } catch (SQLException e) {
                // Handle any SQL exceptions by printing the stack trace
                logger.error("SQL error in getLatestModification for category {}: {}", categoryString, e.getMessage());
            }
        } catch (Exception e) {
            // Handle any SQL exceptions by printing the stack trace
            logger.error("SQL error in getLatestModification for category {}: {}", categoryString, e.getMessage());
        }
        logger.debug("Existing getLatestModification for category {}: {}", categoryString, latestDate);
        // Return the latest modification date, or null if no files are found
        return latestDate;
    }
}

也许我在实例初始化时如何启动连接有问题?

public class DBConnection {

    // The HikariConfig object for configuring the connection pool
    private HikariConfig config;
    private HikariDataSource dataSource;

    // Static initializer for loading the MySQL JDBC driver
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("MySQL JDBC driver not found", e);
        }
    }

public DBConnection(String url, String username, String password) {
        // Create a new DBAccess instance with the specified database connection details
        config = new HikariConfig();

        // Set the URL, username, and password for the database connection
        config.setJdbcUrl(url);
        config.setUsername(username);
        config.setPassword(password);
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");

        dataSource = new HikariDataSource(config);
    }
}
@WebListener
public class WebServletContextListener implements ServletContextListener {
    // Attribute name for the database connection
    private static final String DB_CONNECTION_ATTR = "dbConnection";

@Override
    public void contextInitialized(ServletContextEvent event) {
 // Pass the servlet context to ConfigUtil
            ConfigUtil.setServletContext(event.getServletContext());

            // Create a new DBAccess instance with the specified database connection details
            DBAccess dbConnection = new DBAccess(ConfigUtil.getDatabaseUrl(), ConfigUtil.getDatabaseUsername(),
                    ConfigUtil.getDatabasePassword());
            // Set the DBAccess instance as a servlet context attribute
            event.getServletContext().setAttribute(DB_CONNECTION_ATTR, dbConnection);
}

数据库的日志完全是空的,我不知道我能做什么。

也许你们中的一些人知道如何隔离错误,甚至以前遇到过类似的问题并有解决方案。

问候

解决方案

感谢Thomas Kläger的回复。 这为我解决了这个问题:

try (Connection connection = this.getConnection();
                PreparedStatement preparedStatement = connection.prepareStatement(query))

而不是

 try (Connection connection = this.getConnection();
                PreparedStatement preparedStatement = this.getConnection().prepareStatement(query))
java mysql tomcat jdbc hikaricp
1个回答
0
投票

您在这里泄漏连接:

    try (Connection connection = this.getConnection();
         PreparedStatement preparedStatement = this.getConnection().prepareStatement(query)) {

此片段打开两个连接,但只有一个被声明为要关闭的资源。第二个资源是在第二个连接上创建的

PreparedStatement

解决方案是只打开一个连接并使用该连接来调用

prepareStatement()
:

    try (Connection connection = this.getConnection();
         PreparedStatement preparedStatement = connection.prepareStatement(query)) {
© www.soinside.com 2019 - 2024. All rights reserved.