调用返回游标的PostgreSQL函数时出现<unnamed portal>不存在错误的求助
这个问题我之前也碰到过,核心原因是PostgreSQL的refcursor类型必须在同一个数据库事务上下文里创建和读取。默认情况下,stored-proc-outbound-gateway执行完存储过程后可能会立即提交事务,导致游标被自动关闭,后续读取结果集时就会报“cursor does not exist”的错误。咱们一步步来解决:
1. 给网关添加事务支持
首先要确保整个存储过程调用和结果集读取都处于同一个活跃事务中。你可以通过在链中添加事务拦截器来实现:
先配置事务管理器(如果还没配置的话):
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
然后在你的<int:chain>里加入事务拦截器,把网关包裹在事务中:
<int:chain input-channel="inputDataChannel"> <!-- 事务拦截器,确保整个流程在同一个事务里 --> <int:transactional transaction-manager="transactionManager" propagation="REQUIRED"/> <int-jdbc:stored-proc-outbound-gateway data-source="dataSource" expect-single-result="true" is-function="true" stored-procedure-name="erb.getSampleDetails_sp" ignore-column-meta-data="true"> <!-- 你的参数定义和返回结果集配置保持不变 --> <int-jdbc:sql-parameter-definition name="id" direction="IN"/> <int-jdbc:sql-parameter-definition name="date" direction="IN"/> <int-jdbc:sql-parameter-definition name="name" direction="IN"/> <int-jdbc:sql-parameter-definition name="endrow" type="INTEGER" direction="IN"/> <int-jdbc:sql-parameter-definition name="startrow" type="INTEGER" direction="IN"/> <int-jdbc:sql-parameter-definition name="finalData" direction="OUT"/> <int-jdbc:parameter name="id" expression="headers.xxx.yyy.id"/> <int-jdbc:parameter name="date" expression="headers['documentDate']"/> <int-jdbc:parameter name="name" expression="headers['isCondition']"/> <int-jdbc:parameter name="endrow" expression="headers['endRow']"/> <int-jdbc:parameter name="startrow" expression="headers['startRow']"/> <int-jdbc:returning-resultset name="finalData" row-mapper="sampleRowMapper" /> </int-jdbc:stored-proc-outbound-gateway> </int:chain>
propagation="REQUIRED"会自动创建或加入现有事务,保证游标创建和读取都在同一个事务上下文里。
2. 修正PostgreSQL函数的变量错误+显式命名游标
我注意到你函数里有个小bug:参数是endrow和startrow,但你在LIMIT和WHERE条件里用了i_endrow和i_startrow,这会导致函数执行失败,先把这个改了。另外,显式给游标命名比匿名游标更稳定,避免PostgreSQL自动生成的匿名游标在事务中被意外回收:
修改后的函数:
CREATE OR REPLACE FUNCTION erb.getSampleDetails_sp( id text, date text, name text, endrow double precision, startrow double precision, OUT finalData refcursor ) RETURNS refcursor LANGUAGE 'plpgsql' COST 100 VOLATILE AS $BODY$ BEGIN -- 显式命名游标,比如'sample_details_cursor' finalData := 'sample_details_cursor'; IF name = 'true' THEN OPEN finalData FOR SELECT * FROM ( SELECT row_number() OVER (ORDER BY NULL) AS rnum, vhinv.* FROM (SELECT query) AS vhinv LIMIT endrow ) AS var_sbq WHERE rnum >= startrow; ELSE OPEN finalData FOR SELECT * FROM ( SELECT row_number() OVER (ORDER BY NULL) AS rnum, vhinv.* FROM (SELECT query) AS vhinv LIMIT endrow ) AS var_sbq_2 WHERE rnum >= startrow; END IF; END; $BODY$;
3. 调整网关的元数据配置(可选)
你当前设置了ignore-column-meta-data="true",这可能导致Spring JDBC无法正确识别游标类型。可以尝试把它改成false,让JDBC自动解析参数元数据:
<int-jdbc:stored-proc-outbound-gateway data-source="dataSource" expect-single-result="true" is-function="true" stored-procedure-name="erb.getSampleDetails_sp" ignore-column-meta-data="false"> <!-- 其他配置不变 --> </int-jdbc:stored-proc-outbound-gateway>
4. 检查JDBC驱动版本
确保你用的PostgreSQL JDBC驱动是最新稳定版(比如42.2.x及以上),旧版本在处理游标时可能存在兼容性问题。
总结
最核心的解决点是给整个调用流程加上事务,确保游标创建和读取在同一个事务里。同时修正函数里的变量错误,显式命名游标能让整个流程更稳定。
内容的提问来源于stack exchange,提问作者Abirami

