mybatis存储null到oracle报错问题

本文介绍了在使用MyBatis框架操作Oracle数据库时遇到的问题,当尝试存储NULL值时会出现错误提示。文中详细解释了Oracle数据库对于NULL的理解及如何通过指定JDBC类型来解决这一问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用oracle,在保存用户时,有两个属性分别是String和Date,默认为null,存储时会报如下错误:
 
   无效的列类型: 1111
    Error setting null parameter. Most JDBC drivers require that the JdbcType must be specified for all nullable parameters. Cause: java.sql.SQLException: 无效的列类型: 1111
    ### Error updating database. Cause: org.apache.ibatis.type.TypeException: Error setting null parameter.
经过朋友帮助解决,问题是:在oracle中的null为不确定的意思,存储null时,mybatis解析不到oracle字段类型
解决方法:在插入语句中,加入jdbcType,如下:
#{loginIp,jdbcType=VARCHAR},
#{name,jdbcType=VARCHAR},
<think>我们正在处理一个具体问题:使用MyBatis调用PostgreSQL存储过程时,在存储过程的第43行报错,但直接调用存储过程没有问题。根据引用内容,用户之前使用传统JDBC方式(CallableStatement)调用存储过程是成功的,而使用MyBatis时出现问题问题分析: 1. 直接调用存储过程没有问题,说明存储过程本身在数据库层面是正常的。 2. 使用MyBatis调用时出现错误,问题可能出在MyBatis的调用方式上。 根据引用[1]和引用[3][4]中关于MyBatis调用存储过程的示例,我们可以推测用户可能在使用MyBatis调用存储过程时,映射方式或参数传递有误。 解决方案步骤: 第一步:检查MyBatis映射文件(或注解)中调用存储过程的语法 在PostgreSQL中,调用存储过程的语法与Oracle略有不同。根据引用[1]中传统方式的调用: { ? = call bin_info.getbname( ? ) } 在MyBatis中,我们可以使用`select`语句(如果函数返回结果集)或者使用`selectCall`(存储过程)方式。但注意,PostgreSQL中存储过程(PROCEDURE)和函数(FUNCTION)调用方式不同。在PostgreSQL 11之前,只有函数(FUNCTION),而存储过程(PROCEDURE)是PostgreSQL 11引入的,支持事务。 如果用户使用的是函数(FUNCTION),则应该使用: select * from function_name(参数) 如果用户使用的是存储过程(PROCEDURE),则应该使用: call procedure_name(参数) 但是,MyBatis中调用存储过程通常使用`CALL`语句,或者使用`{call ...}`。 第二步:检查参数传递 存储过程可能包含日期参数(从存储过程名称`proc_table_spark_build_stat_d(date)`可以看出)。在MyBatis中传递日期参数时,需要确保日期格式正确,并且类型匹配。 第三步:捕获详细错误 在MyBatis的配置中,启用更详细的日志,查看实际发送到数据库的SQL语句以及错误信息。 具体步骤: 1. 在MyBatis配置文件中,设置日志级别为DEBUG(或者使用logback/log4j配置): ```xml <configuration> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> </configuration> ``` 2. 在代码中捕获异常,并打印完整的异常堆栈。 3. 检查MyBatis映射文件(XML)或注解中调用存储过程的语句是否正确。 示例(使用XML方式调用存储过程): 对于函数(返回结果): ```xml <select id="callFunction" resultType="java.lang.String" statementType="CALLABLE"> { #{result, mode=OUT, jdbcType=VARCHAR} = call bin_info.getbname(#{id, mode=IN}) } </select> ``` 或者对于存储过程(无返回值或输出参数): ```xml <select id="callProcedure" statementType="CALLABLE"> { call bin_info.getbname(#{id, mode=IN}, #{result, mode=OUT, jdbcType=VARCHAR}) } </select> ``` 注意:在PostgreSQL中,函数可以使用`=`来接收返回值,而存储过程使用`call`且不能直接使用`=`接收返回值(存储过程通过OUT参数返回)。 根据错误信息,存储过程是`opr_stat.proc_table_spark_build_stat_d(date)`,它可能是一个存储过程(PROCEDURE)或者函数(FUNCTION)?从名称看,它可能是一个存储过程(因为以proc_开头),并且参数只有一个日期。 如果它是一个存储过程(PROCEDURE),那么调用方式应该是: call opr_stat.proc_table_spark_build_stat_d(#{dateParam}) 但是,存储过程也可以有输出参数。如果存储过程有输出参数,那么需要按照输出参数的方式传递。 第四步:检查存储过程定义 直接查看存储过程的定义,特别是第43行的SQL语句是什么。注意,在PL/pgSQL中,SQL语句必须使用`PERFORM`来执行不返回结果的SQL,或者使用`SELECT ... INTO`来执行返回单行的SQL。如果第43行是一个SQL语句,那么可能是: - 如果是SELECT语句且没有INTO,则必须使用PERFORM。 - 或者是一个DML语句(INSERT/UPDATE/DELETE)? 但直接调用没有问题,说明存储过程定义没有问题。那么问题可能在于MyBatis传递的参数类型不正确。 第五步:参数类型处理 在MyBatis中,传递日期参数时,确保使用正确的类型。例如,如果参数是`java.util.Date`,那么需要指定`jdbcType=DATE`(如果只包含日期部分)或`jdbcType=TIMESTAMP`(包含时间部分)。 示例: ```xml <select id="callProcedure" statementType="CALLABLE"> { call opr_stat.proc_table_spark_build_stat_d(#{dateParam, jdbcType=DATE}) } </select> ``` 或者,如果存储过程有输出参数,那么需要指定输出参数: ```xml <select id="callProcedure" statementType="CALLABLE"> { call opr_stat.proc_table_spark_build_stat_d(#{dateParam, jdbcType=DATE}, #{result, mode=OUT, jdbcType=INTEGER}) } </select> ``` 具体取决于存储过程的定义。 第六步:使用注解方式 也可以尝试使用注解方式调用,例如: ```java @Options(statementType = StatementType.CALLABLE) @Select("{call opr_stat.proc_table_spark_build_stat_d(#{dateParam, jdbcType=DATE})}") void callProcedure(Date dateParam); ``` 但注意,如果存储过程有输出参数,注解方式需要配合`@Param`注解。 总结: 由于直接调用存储过程没有问题,而MyBatis调用有问题,最可能的原因是: 1. MyBatis调用存储过程的语法不正确(函数与存储过程调用混淆)。 2. 参数传递不正确(特别是日期类型)。 3. 没有正确处理输出参数(如果存储过程有输出参数)。 因此,请用户检查: - 存储过程是函数(FUNCTION)还是存储过程(PROCEDURE)?这决定了调用语法。 - 在MyBatis映射中,参数类型是否匹配(特别是日期类型)? - 是否配置了正确的日志级别以查看实际执行的SQL? 如果问题仍然存在,请提供存储过程的定义(特别是第43行附近)以及MyBatis的映射配置(XML或注解),以便进一步分析。 相关问题: 1. MyBatis调用PostgreSQL存储过程和函数有什么区别? 2. 如何在MyBatis中传递日期参数并指定jdbcType? 3. 如何配置MyBatis的日志级别以查看执行的SQL? 4. 存储过程中SQL语句的正确写法(PL/pgSQL)? 5. 如何捕获MyBatis调用存储过程时的详细错误信息?</think>### MyBatis调用PostgreSQL存储过程异常分析与解决方案 #### 问题诊断 当MyBatis调用PostgreSQL存储过程在line 43报错,但直接执行无问题时,通常由以下原因导致: 1. **参数类型不匹配** MyBatis在映射参数时可能未正确处理PostgreSQL的日期类型,导致存储过程内部SQL执行异常[^2]。 2. **事务隔离问题** MyBatis默认自动提交事务,而存储过程可能需要显式事务控制(如`BEGIN...COMMIT`块)。 3. **输出参数处理错误** 存储过程如果有OUT参数,MyBatis需特殊配置才能正确接收返回值[^1][^4]。 4. **调用语法差异** 直接执行使用`SELECT proc_name()`,而MyBatis需使用`CALL`或函数语法[^1][^3]。 #### 解决方案 ##### 第一步:正确配置存储过程调用 在Mapper XML中使用PostgreSQL兼容语法: ```xml <!-- 使用函数调用语法 --> <select id="callProcedure" resultType="map" statementType="CALLABLE"> SELECT * FROM opr_stat.proc_table_spark_build_stat_d( #{dateParam, jdbcType=DATE, mode=IN} ) </select> <!-- 或使用CALL语法(PG11+) --> <select id="callProcedure" statementType="CALLABLE"> CALL opr_stat.proc_table_spark_build_stat_d(#{dateParam}) </select> ``` > **关键点**: > - 使用`statementType="CALLABLE"`声明存储过程调用 > - 显式指定`jdbcType=DATE`避免类型推断错误[^2] > - 返回类型设为`map`可接收输出参数[^4] ##### 第二步:添加事务控制注解 在Service层添加Spring事务管理: ```java @Transactional public void executeProcedure(LocalDate statDate) { Map<String, Object> params = new HashMap<>(); params.put("dateParam", statDate); mapper.callProcedure(params); // 获取输出参数 Object result = params.get("result"); } ``` ##### 第三步:调试存储过程内部错误 1. 在PL/pgSQL中添加详细日志: ```sql CREATE OR REPLACE PROCEDURE opr_stat.proc_table_spark_build_stat_d(stat_date DATE) LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'Start processing: %', stat_date; -- 第43行前添加 -- 原第43行SQL INSERT INTO stat_table(...) VALUES (...); RAISE NOTICE 'Completed: %', stat_date; EXCEPTION WHEN OTHERS THEN RAISE EXCEPTION 'Line43 Error: %', SQLERRM; END; $$; ``` 2. 在MyBatis中捕获详细错误: ```java try { mapper.callProcedure(statDate); } catch (PersistenceException e) { SQLException sqlEx = (SQLException) e.getCause(); while (sqlEx != null) { log.error("SQLState: {}, Message: {}", sqlEx.getSQLState(), sqlEx.getMessage()); sqlEx = sqlEx.getNextException(); } } ``` ##### 第四步:参数类型强制转换 在传递日期参数时显式转换: ```java // Java 8+ 日期类型处理 LocalDate statDate = LocalDate.of(2023, 1, 15); // 传统Date类型处理 Date utilDate = Date.valueOf(statDate); params.put("dateParam", new java.sql.Date(utilDate.getTime())); ``` #### 验证方案 1. **日志对比** 检查MyBatis日志中的实际执行SQL: ``` ==> Preparing: {call opr_stat.proc_table_spark_build_stat_d(?)} ==> Parameters: 2023-01-15(Date) ``` 2. **参数嗅探** 在存储过程中添加参数检查: ```sql IF stat_date IS NULL THEN RAISE EXCEPTION 'stat_date cannot be null'; END IF; ``` 3. **权限验证** 确保MyBatis使用的数据库用户有执行权限: ```sql GRANT EXECUTE ON PROCEDURE opr_stat.proc_table_spark_build_stat_d TO mybatis_user; ``` #### 典型错误场景处理 | 错误类型 | 解决方案 | |-------------------------|-------------------------------------------------------------------------| | 日期格式不匹配 | 使用`java.sql.Date`代替`java.util.Date` | | 输出参数未注册 | 在Mapper中添加`@Options`声明输出参数[^4] | | 存储过程版本不一致 | 检查数据库连接指向正确环境`SELECT current_database()` | | 空指针异常 | 添加参数非空检查`<if test="dateParam != null">...</if>` | > **最佳实践**:对于复杂存储过程调用,推荐使用MyBatis的`@SelectProvider`动态生成SQL: > ```java > @SelectProvider(type = ProcedureProvider.class, method = "buildStatSQL") > void callProcedure(@Param("dateParam") LocalDate date); > > class ProcedureProvider { > public String buildStatSQL(LocalDate date) { > return "SELECT * FROM opr_stat.proc_table_spark_build_stat_d('" > + date.toString() + "')"; > } > } > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值