Caused by: org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.

Caused by: org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:382)
        at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:490)
        at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:408)
        at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:166)
        at org.postgresql.jdbc.PgPreparedStatement.execute(PgPreparedStatement.java:155)
        at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3446)
        at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter.java:434)
        at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3444)
        at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:152)
        at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:483)
        at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
        at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
        at sun.reflect.GeneratedMethodAccessor151.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:64)
        at com.sun.proxy.$Proxy295.query(Unknown Source)
        at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
        at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325)
        at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
        at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:81)
        at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:62)
        at com.sun.proxy.$Proxy294.query(Unknown Source)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
        at sun.reflect.GeneratedMethodAccessor149.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427)
        ... 25 common frames omitted
Caused by: java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 39176
        at org.postgresql.core.PGStream.sendInteger2(PGStream.java:359)
        at org.postgresql.core.v3.QueryExecutorImpl.sendParse(QueryExecutorImpl.java:1604)
        at org.postgresql.core.v3.QueryExecutorImpl.sendOneQuery(QueryExecutorImpl.java:1929)
        at org.postgresql.core.v3.QueryExecutorImpl.sendQuery(QueryExecutorImpl.java:1487)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:347)
        ... 54 common frames omitted

遇到这种错误该如何解决呢?

分析:此错误的核心原因是 ​PostgreSQL JDBC驱动对协议参数值的限制。具体表现为:

  1. 参数值溢出:错误日志中的 Tried to send an out-of-range integer as a 2-byte value: 39176 表明程序尝试发送的整数值超过了 2 字节(即超过 32767)的表示范围。
  2. 批量操作数据量过大:常见于批量插入或查询时,单次传输的 SQL 参数数量或数据行数超过 PostgreSQL 协议限制。

解决方式:检查代码,看是否有查询之类的语句导致语句量过大,导致资源耗尽。

问题示例:

// 省去service层查询,直接通过wrapper条件进行查询
        LambdaQueryWrapper<SlotEntity> queryWrapper = Wrappers.lambdaQuery();
        mapSlotPairs.forEach(item -> {
            queryWrapper.or(wrapper -> wrapper.eq(SlotEntity::getMapCode, item.get("mapCode")).eq(SlotEntity::getSlotCode, item.get("slotCode")));
        });
        List<SlotEntity> oldSlotEntityList = SLOT_SERVICE.list(queryWrapper);

原因:我表中的数据量有2w条,导致查询语句过长,超过了pgsql默认限制。

解决方式:(批量处理)

// 每批次处理 1000 条
int batchSize = 1000;
List<SlotEntity> oldSlotEntityList = new ArrayList<>();
for (int i = 0; i < mapSlotPairs.size(); i += batchSize) {
    List<Map<String, String>> subList = mapSlotPairs.subList(i, Math.min(i + batchSize, mapSlotPairs.size()));
    LambdaQueryWrapper<SlotEntity> batchWrapper = Wrappers.lambdaQuery();
    subList.forEach(item -> {
        batchWrapper.or(wrapper -> wrapper
                .eq(SlotEntity::getMapCode, item.get("mapCode"))
                .eq(SlotEntity::getSlotCode, item.get("slotCode")));
    });
    oldSlotEntityList.addAll(SLOT_SERVICE.list(batchWrapper));
}
### 解决 PostgreSQL 中插入数据时因 `location` 字段为空导致的 NOT-NULL 约束错误 当向 PostgreSQL 数据库插入数据时,如果某个字段设置了 `NOT NULL` 约束而未提供有效值,则会抛出类似于以下的异常: ```plaintext org.postgresql.util.PSQLException: ERROR: null value in column "location" of relation "table_name" violates not-null constraint. ``` 此问题的根本原因在于目标表中的某些列被定义为不可接受空值 (`NOT NULL`),但在执行插入操作时并未为其指定有效的非空值。 #### 可能的原因分析 1. **字段缺少默认值** 如果 `location` 列没有设置默认值,在尝试插入记录时不显式赋值给该列,就会触发上述错误[^1]。 2. **应用程序逻辑缺失** 应用程序可能未能正确处理必填字段的情况,从而导致传递的数据不完整[^2]。 3. **迁移脚本配置不当** 在创建或修改表结构的过程中,可能存在遗漏,默认情况下将新添加的列设为了 `NOT NULL` 而又无合理默认值设定[^3]。 --- ### 解决方案 以下是几种常见的解决方案及其适用场景: #### 方法一:更新表结构以允许 NULL 值或者设置默认值 可以通过 SQL 修改语句调整现有表的设计来适应业务需求。例如: - 将 `location` 设置成可以接收 NULL 的状态: ```sql ALTER TABLE table_name ALTER COLUMN location DROP NOT NULL; ``` - 或者为 `location` 提供一个合理的默认值(比如字符串 'unknown' 表示未知位置): ```sql ALTER TABLE table_name ALTER COLUMN location SET DEFAULT 'unknown'; ``` 这两种方式都可以防止再次发生由于 `location` 缺失而导致的插入失败情况。 #### 方法二:修正应用层提交的数据 确保所有必要的参数都已填充后再发送至数据库端。对于前端传来的请求对象,应该增加校验机制确认其完整性;如果是后台批量导入流程则需提前清洗源文件内容保证每条记录均满足目标表格的要求。 另外还可以考虑利用 ORM 框架内置验证功能自动拦截不符合条件的对象实例化过程并提示开发者修复原始输入材料中存在的缺陷之处。 #### 方法三:初始化序列号匹配当前最大ID值(针对主键冲突情形) 虽然题目主要讨论的是关于 `location` 属性违反非空规定的问题,但是也提到了有关重复关键字违背唯一索引限制的信息。因此这里额外补充一点相关内容——即如何同步手动维护的自增型标识符与其实际使用的数值范围保持一致: 假设存在名为 `device_id_seq` 的序列用于生成设备编号系列,那么可通过如下命令将其重置到最新可用高度之上: ```sql SELECT MAX(id)+1 FROM device INTO TEMPORARY temp_max; -- 获取最高现存 ID 加一作为新的起始点 PERFORM setval('device_id_seq', COALESCE((SELECT * FROM temp_max), 1));-- 更新序列起点 DROP TABLE IF EXISTS temp_max CASCADE ; -- 清理临时存储单元 ``` 这样做的目的是为了避免未来新增实体因为 PK 自动分配越界造成进一步碰撞现象的发生. --- ### 示例代码片段展示具体实现细节 下面给出一段 Python 使用 psycopg2 进行安全写入的例子,其中包含了基本的事前检查以及事后回滚策略保障事务一致性的同时规避潜在风险因素的影响。 ```python import psycopg2 from psycopg2 import sql, extensions def safe_insert_record(conn_string, table_name, data_dict): try: conn = psycopg2.connect(conn_string) cursor = conn.cursor() columns = ', '.join(data_dict.keys()) placeholders = ', '.join(['%s'] * len(data_dict)) values = tuple(data_dict.values()) query_template = f""" INSERT INTO {table_name} ({columns}) VALUES ({placeholders}) ON CONFLICT DO NOTHING RETURNING id; """ result = None with conn.transaction(): cursor.execute(query_template,values) result = cursor.fetchone()[0] return {"status":"success","message":f"Inserted row with ID={result}"} except Exception as e: if isinstance(e,(extensions.DatabaseError)): raise RuntimeError(f'Database operation failed due to:{str(e)}') else : raise ValueError(f'Invalid parameter supplied:{data_dict}') finally: if 'conn' in locals() and not conn.closed: conn.close() if __name__ == '__main__': sample_data={'name':'test','location':'somewhere'} res=safe_insert_record("dbname=mydb user=user password=secret", "devices",sample_data ) print(res) ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值