HikariCP 5.0.1搭配MySQL 8.0.27执行更新时连接不可用问题
问题描述
我正在开发基于JDK 17、Tomcat 10.0.5的Java Web应用,使用mysql-connector-j-8.0.31连接MySQL 8.0.27数据库。JDBC直接管理物理连接时一切正常,但使用HikariCP管理连接时,调用CallableStatement和PreparedStatement的executeUpdate()会出现连接不可用错误,executeQuery()却能正常运行。
环境信息
- JDK: 17
- Tomcat: 10.0.5
- HikariCP: 5.0.1
- MySQL: 8.0.27
- mysql-connector-j: 8.0.31
JNDI资源配置
<Resource name="jdbc/MySQLHikari" auth="Container" factory="com.zaxxer.hikari.HikariJNDIFactory" type="javax.sql.DataSource" minimumIdle="5" maximumPoolSize="10" idleTimeout="120000" connectionTimeout="300000" leakDetectionThreshold="300000" dataSourceClassName="com.mysql.cj.jdbc.MysqlDataSource" dataSource.serverName="127.0.0.1" dataSource.port="3307" dataSource.databaseName="optica?useSSL=false&useUnicode=true&characterEncoding=utf-8" dataSource.user="root" dataSource.password="root" dataSource.cachePrepStmts="true" dataSource.prepStmtCacheSize="250" dataSource.prepStmtCacheSqlLimit="2048" dataSource.useServerPrepStmts="true" dataSource.useLocalSessionState="true" dataSource.rewriteBatchedStatements="true" dataSource.cacheResultSetMetadata="true" dataSource.cacheServerConfiguration="true" dataSource.elideSetAutoCommits="true" dataSource.maintainTimeStats="false" />
连接池获取代码
public class ConexionMySQLProvider { private static HikariDataSource hikariDataSource; private static void buildContext() { try { Context context = (Context) new InitialContext().lookup("java:/comp/env"); DataSource dataSource = (DataSource) context.lookup("jdbc/MySQLHikari"); HikariConfig config = new HikariConfig(); config.setDataSource(dataSource); hikariDataSource = new HikariDataSource(config); } catch (Exception e) { e.printStackTrace(); } } public static Connection getFromPool() throws Exception { if (hikariDataSource == null) { buildContext(); } return hikariDataSource.getConnection(); } }
问题代码片段
public class ControllerCliente { public int insert(Cliente c) throws Exception { String sql = "{call insertarCliente(?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?)}"; Connection conn = ConexionMySQLProvider.getFromPool(); CallableStatement cstmt = conn.prepareCall(sql); int idClienteGenerado = -1; // 填充CallableStatement参数 cstmt.setString(1, c.getPersona().getNombre()); ... // 执行更新,此处报错连接不可用 cstmt.executeUpdate(); // 回收生成的ID idClienteGenerado = -1; ... cstmt.close(); conn.close(); return idClienteGenerado; } public void delete(int id) throws Exception { String sql = "UPDATE cliente SET estatus=0 WHERE idCliente=?"; Connection conn = ConexionMySQLProvider.getFromPool(); PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, id); // 执行更新,此处报错连接不可用 pstmt.executeUpdate(); pstmt.close(); conn.close(); } public List<Cliente> getAll(String filtro) throws Exception { String sql = "SELECT * FROM v_clientes WHERE estatus=1"; Connection conn = ConexionMySQLProvider.getFromPool(); PreparedStatement pstmt = conn.prepareStatement(sql); // 查询正常执行 ResultSet rs = pstmt.executeQuery(); List<Cliente> clientes = new ArrayList<>(); while (rs.next()) clientes.add(fill(rs)); rs.close(); pstmt.close(); conn.close(); return clientes; } private Cliente fill(ResultSet rs) throws Exception { Cliente c = new Cliente(); // 从ResultSet填充Cliente对象 ... return c; } }
已尝试方案
- 查阅HikariCP官方配置文档,调整连接池参数(最大连接数、最小空闲数等),无效果;
- 使用try-with-resources语法自动关闭资源,问题依旧;
- 降级mysql-connector至5.1.36,无法连接MySQL 8.0.27;
- 替换为MariaDB JDBC驱动,问题复现;
- 切换至C3P0连接池,多次执行
executeUpdate()后仍出现连接不可用问题,故转回HikariCP 5.0.1。
约束条件
- 必须使用MySQL 8.X版本;
- 应用需基于Java 17开发,未来计划迁移至Java 19。
解决建议
1. 修复连接池获取代码的核心错误
你当前的ConexionMySQLProvider存在严重逻辑问题:通过JNDI获取的DataSource已经是HikariCP实例(因为配置了factory="com.zaxxer.hikari.HikariJNDIFactory"),但你又用这个数据源重新创建了一层HikariDataSource,相当于嵌套连接池,直接导致连接管理混乱,这是引发连接不可用的核心原因。
修改后的代码应直接复用JNDI返回的数据源:
public class ConexionMySQLProvider { private static DataSource dataSource; private static void buildContext() { try { Context context = (Context) new InitialContext().lookup("java:/comp/env"); dataSource = (DataSource) context.lookup("jdbc/MySQLHikari"); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("初始化数据源失败", e); } } public static Connection getFromPool() throws Exception { if (dataSource == null) { buildContext(); } return dataSource.getConnection(); } }
2. 修正JNDI配置中的数据库参数错误
你的dataSource.databaseName配置里包含了URL参数(?useSSL=false...),这是不符合规范的。databaseName应仅填写数据库名,额外连接参数需单独配置:
<Resource name="jdbc/MySQLHikari" auth="Container" factory="com.zaxxer.hikari.HikariJNDIFactory" type="javax.sql.DataSource" minimumIdle="5" maximumPoolSize="10" idleTimeout="120000" connectionTimeout="300000" leakDetectionThreshold="300000" dataSourceClassName="com.mysql.cj.jdbc.MysqlDataSource" dataSource.serverName="127.0.0.1" dataSource.port="3307" dataSource.databaseName="optica" dataSource.user="root" dataSource.password="root" dataSource.useSSL="false" dataSource.useUnicode="true" dataSource.characterEncoding="utf-8" dataSource.cachePrepStmts="true" dataSource.prepStmtCacheSize="250" dataSource.prepStmtCacheSqlLimit="2048" dataSource.useServerPrepStmts="true" dataSource.useLocalSessionState="true" dataSource.rewriteBatchedStatements="true" dataSource.cacheResultSetMetadata="true" dataSource.cacheServerConfiguration="true" dataSource.elideSetAutoCommits="true" dataSource.maintainTimeStats="false" />
3. 显式控制连接的自动提交状态
默认JDBC连接的autoCommit为true,但连接池返回的连接可能存在状态异常,建议在获取连接后显式设置:
Connection conn = ConexionMySQLProvider.getFromPool(); conn.setAutoCommit(true);
4. 开启HikariCP日志排查连接泄漏
在Tomcat的logging.properties中添加以下配置,开启HikariCP的DEBUG日志,跟踪连接的创建、获取、归还流程:
com.zaxxer.hikari.level = FINE
内容的提问来源于stack exchange,提问作者LiveGrios
相关产品推荐
相关产品推荐

