Apache DBCP 2问题:使用含数组的PreparedStatement时创建新会话
嘿,我来帮你搞定这个困扰你的问题!结合你用Spring Boot、DBCP2和PostgreSQL 10的技术栈,我整理了几个针对性的解决方案:
方案一:改用Spring Boot自动配置的JdbcTemplate(最省心)
你现在手动写getJdbcTemplate() Bean的方式容易漏掉DBCP2和PostgreSQL适配的关键配置。Spring Boot本身就支持自动配置DBCP2连接池和JdbcTemplate,能帮你自动处理数组类型的连接复用问题:
- 删掉你手动编写的
getJdbcTemplate()Bean - 在
application.yml(或application.properties)里配置数据库连接:
spring: datasource: url: jdbc:postgresql://你的数据库地址:5432/你的库名 username: ${user} password: ${password} driver-class-name: org.postgresql.Driver dbcp2: application-name: ${applicationName} # 可选:配置连接池参数,确保连接复用效率 max-total: 10 max-idle: 5 # 初始化连接时执行SQL,确保会话状态正确 connection-init-sql: SELECT 1;
Spring Boot会自动帮你创建适配PostgreSQL的DBCP2数据源和JdbcTemplate,从根源上避免连接无法复用的问题。
方案二:手动配置时修复连接池的PostgreSQL适配
如果你一定要手动创建JdbcTemplate,那得用DBCP2标准的BasicDataSource来封装连接池逻辑,而不是手动拼接ConnectionFactory:
修改你的getJdbcTemplate()方法:
@Bean public JdbcTemplate getJdbcTemplate() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("org.postgresql.Driver"); dataSource.setUrl("jdbc:postgresql://你的数据库地址:5432/你的库名"); dataSource.setUsername(user); dataSource.setPassword(password); // 设置应用名称属性 dataSource.addConnectionProperty(PGProperty.APPLICATION_NAME.getName(), applicationName); // 初始化连接时执行SQL,确保会话状态可复用 dataSource.setConnectionInitSql("SELECT 1"); // 根据业务需求调整自动提交设置 dataSource.setDefaultAutoCommit(true); return new JdbcTemplate(dataSource); }
BasicDataSource已经封装了DBCP2完整的连接复用、状态重置逻辑,能正确处理PostgreSQL数组类型的连接状态,避免每次创建新会话。
方案三:检查数组类型的PreparedStatement写法
另外,你处理数组参数的代码也可能影响连接复用,确保用正确的方式传入数组:
方式1:使用JdbcTemplate的通用参数传入
jdbcTemplate.update( "INSERT INTO your_table (array_column) VALUES (?)", new Object[]{new String[]{"a", "b", "c"}}, new int[]{Types.ARRAY} );
方式2:获取原生PGConnection创建数组
如果需要更精细的控制,正确unwrap DBCP2包装的连接,不要每次新建连接:
jdbcTemplate.update(conn -> { PreparedStatement ps = conn.prepareStatement("INSERT INTO your_table (array_column) VALUES (?)"); // 正确获取原生PostgreSQL连接 PGConnection pgConn = conn.unwrap(PGConnection.class); Array array = pgConn.createArrayOf("text", new String[]{"a", "b", "c"}); ps.setArray(1, array); return ps; });
问题根源
你之前手动创建ConnectionFactory的方式,没有利用DBCP2完整的连接池状态管理机制。当执行数组相关的PreparedStatement时,连接的特定状态(比如PostgreSQL的类型注册)没有被正确保存或重置,连接池认为该连接无法复用,就会创建新的会话。
内容的提问来源于stack exchange,提问作者Johannes Flügel

