OpenTracing+Spring事务中JDBC连接提前关闭的问题及方案咨询
解决方案:修复OpenTracing JDBC连接在事务中的比较问题
针对你遇到的NamedParameterJdbcTemplate#queryForStream在事务中导致连接比较不一致的问题,以下是几个可行的解决方案:
方案一:自定义TracingConnection与TracingPreparedStatement,修正getConnection()返回值
问题核心在于TracingConnection创建的PreparedStatement调用getConnection()时返回的是原始的Hikari代理连接,而非TracingConnection实例。通过自定义包装类,让PreparedStatement返回TracingConnection,保证与ConnectionHolder中的连接实例一致:
1. 自定义TracingConnection,重写prepareStatement方法
public class CustomTracingConnection extends TracingConnection { public CustomTracingConnection(Connection connection, String dbType, Tracer tracer) { super(connection, dbType, tracer); } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { PreparedStatement delegate = super.prepareStatement(sql); return new CustomTracingPreparedStatement(delegate, this, getDbType(), getTracer()); } // 按需重写其他prepareStatement重载方法(如带参数、返回生成键的版本) }
2. 自定义TracingPreparedStatement,重写getConnection()
public class CustomTracingPreparedStatement extends TracingPreparedStatement { private final CustomTracingConnection tracingConnection; public CustomTracingPreparedStatement(PreparedStatement delegate, CustomTracingConnection tracingConnection, String dbType, Tracer tracer) { super(delegate, tracingConnection.getDelegate(), dbType, tracer); this.tracingConnection = tracingConnection; } @Override public Connection getConnection() throws SQLException { // 返回TracingConnection实例而非原始连接 return tracingConnection; } }
3. 自定义TracingDataSource,使用自定义连接包装类
public class CustomTracingDataSource extends TracingDataSource { public CustomTracingDataSource(DataSource dataSource, String dbType, Tracer tracer) { super(dataSource, dbType, tracer); } @Override protected Connection decorateConnection(Connection connection) { return new CustomTracingConnection(connection, getDbType(), getTracer()); } }
4. Spring配置中替换数据源
将原有Hikari数据源包装为自定义的CustomTracingDataSource,确保所有获取的连接都是自定义包装实例:
@Bean public DataSource dataSource(Tracer tracer) { HikariDataSource hikariDs = new HikariDataSource(); // 配置Hikari连接参数(URL、用户名、密码等) return new CustomTracingDataSource(hikariDs, "postgresql", tracer); }
方案二:重写TracingConnection的equals/hashCode方法,基于原始连接比较
如果不想修改PreparedStatement的逻辑,可以让TracingConnection在比较时基于unwrap后的原始连接判断,这样即使传入的是Hikari代理连接,只要原始连接一致就会被判定为相等:
public class CustomTracingConnection extends TracingConnection { // 构造方法同方案一 @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Connection)) return false; try { Connection thisRawConn = this.unwrap(Connection.class); Connection otherRawConn = ((Connection) obj).unwrap(Connection.class); return thisRawConn.equals(otherRawConn); } catch (SQLException e) { // 异常时 fallback到默认实例比较 return super.equals(obj); } } @Override public int hashCode() { try { return this.unwrap(Connection.class).hashCode(); } catch (SQLException e) { return super.hashCode(); } } }
这个方案不需要修改PreparedStatement,直接让DataSourceUtils.connectionEquals通过equals方法判断连接是否一致。
方案三:升级opentracing-contrib-jdbc版本
检查当前使用的opentracing-contrib-jdbc版本,部分旧版本存在PreparedStatement.getConnection()返回原始连接的bug。尝试升级到最新稳定版(如0.2.0及以上),看是否已修复该问题,无需自定义代码即可解决。
内容的提问来源于stack exchange,提问作者DOKL57
相关产品推荐
相关产品推荐

