Yesterday

昨天给自己的MSN改名“今年不过圣诞”。既然决定不过就不过了,可是饭还得照吃,本想晚上出去吃的,不是因为圣诞而是因为周末的缘故,但是看到连思得客这种地方人都爆满了,我们只好打了退堂鼓,怎么办总不能不吃吧?于是几个人密谋了一下决定在实验室里吃火锅,哈哈,好提议。于是我们4个人立刻分工。男生负责起火,女生负责采购。来到超市还真吓一跳,青菜都剩不下几包了,挑了挑捡了捡,终于满载而归,回来的路上看到很多店的门口全是排的长龙,似乎都不怕冷的,唉!真不理解,一个圣诞,把人都搞得疯狂成这样.....
很快就可以开吃了,哈哈,我们4人一桌边吃边看《天下无贼》,同时还不忘嘲笑一下在外面挨冻挨饿的那些傻瓜们,哈哈,真高兴。
在同一天,先是去影城看的《功夫》,然后又是电脑上看了《无贼》,相比之下,两部影片都还不错,只是不能比较,因为他们的性质不同,只是有点遗憾刘德华的结局,真的好希望他们能够善有善报,同时也觉得刘若英的演技比在《20,30,40》里有所提高,赞一下。
晚饭从7点吃到10点,他们提出要打牌,输的洗锅并负责所有的开销,被我以刚吃完饭大脑缺氧为由言辞决绝,呵呵。
有朋友约出去唱歌,可见现代人的生活是多么的贫乏,除了吃饭唱歌打牌“杀人”之外,还有什么?不想去通宵,觉得很伤身体。
在网上和小宝聊到1点多,觉得也很晚了,该回去了,不知道舍友都睡了没。蹑手蹑脚的回到宿舍,打开门一看,没人?又是一个通宵的主。不管了,洗洗赶紧睡觉了。
今天醒得很早,去吃早餐,路上看到很多人睡眼朦胧的样子,不用问,一定是刚疯回来的,瞬间觉得自己生活的很健康,^_^。吃了一个早餐套餐后,来到实验室,写下这片blog,然后开始工作。
还有很重要的一点是,我曾在今年7月份的时候预言冯小刚的这部电影不信你看这里
<think>我们已知用户在使用MyBatis调用PostgreSQL存储过程时遇到错误,存储过程名为`opr_stat.proc_table_spark_build_stat_d(date)`,参数为'yesterday'。之前已经给出了一些解决方案,但用户仍然遇到问题,现在需要继续排查。 根据之前的分析,可能的原因包括: 1. 参数类型问题:存储过程需要`date`类型,但传递的'yesterday'可能被解释为字符串。 2. 存储过程内部逻辑错误,在直接调用时可能因为环境不同(如数据状态)而没有报错,但通过MyBatis调用时因为参数传递方式导致问题。 3. MyBatis配置问题,如事务、调用方式等。 用户提到参数是'yesterday',这很可能是一个字符串,而存储过程需要的是date类型。在PostgreSQL中,我们可以使用表达式如`CURRENT_DATE - INTERVAL '1 day'`来获取昨天的日期。因此,在MyBatis中传递字符串'yesterday'是不合适的。 解决方案: 1. 在Java代码中计算昨天的日期,然后将java.sql.Date对象传递给存储过程。 2. 修改MyBatis的调用,使用PostgreSQL的表达式将字符串'yesterday'转换为日期,但这种方法不推荐,因为存储过程要求date类型,且直接传递字符串可能会导致类型不匹配。 步骤: 第一步:在Java代码中获取昨天的日期 ```java import java.time.LocalDate; // 在调用存储过程的方法中 LocalDate yesterday = LocalDate.now().minusDays(1); ``` 第二步:在MyBatis的Mapper中,使用正确的参数类型传递 在Mapper接口中: ```java void callBuildStatD(@Param("statDate") Date statDate); ``` 在Mapper XML中(使用函数调用方式,因为存储过程在PostgreSQL中实际上可以作为函数调用): ```xml <select id="callBuildStatD" statementType="CALLABLE"> SELECT opr_stat.proc_table_spark_build_stat_d(#{statDate, jdbcType=DATE}) </select> ``` 或者,如果使用存储过程语法(PostgreSQL 11及以上支持): ```xml <select id="callBuildStatD" statementType="CALLABLE"> CALL opr_stat.proc_table_spark_build_stat_d(#{statDate, jdbcType=DATE}) </select> ``` 第三步:调用Mapper方法 ```java // 将LocalDate转换为java.sql.Date java.sql.Date sqlDate = java.sql.Date.valueOf(yesterday); mapper.callBuildStatD(sqlDate); ``` 如果仍然报错,我们需要进一步检查存储过程内部。根据错误信息,错误发生在存储过程的第43行。我们可以尝试在数据库中直接调用存储过程,使用相同的日期参数,看是否报错。 例如,在psql中执行: ```sql SELECT opr_stat.proc_table_spark_build_stat_d(CURRENT_DATE - INTERVAL '1 day'); ``` 或者 ```sql CALL opr_stat.proc_table_spark_build_stat_d(CURRENT_DATE - INTERVAL '1 day'); -- PostgreSQL 11+ ``` 如果直接调用也报错,那么问题在存储过程本身。如果直接调用成功,那么问题在于MyBatis的调用方式。 另外,注意存储过程的权限问题,确保MyBatis使用的数据库用户有执行存储过程的权限。 如果存储过程内部报错,我们可以通过添加日志或检查存储过程代码来定位。例如,在存储过程中添加RAISE NOTICE语句,或者使用EXCEPTION块捕获错误并打印详细信息。 如果存储过程内部第43行是一个SQL语句,可能是该SQL语句在特定参数下执行失败(例如,数据不存在,违反约束等)。因此,需要检查该SQL语句。 总结步骤: 1. 确保传递正确的日期类型参数(java.sql.Date,值为昨天的日期)。 2. 检查存储过程权限。 3. 在数据库中直接使用相同的参数调用存储过程,验证是否成功。 4. 如果直接调用成功,则检查MyBatis的配置(如事务、调用方式等);如果直接调用失败,则调试存储过程。 如果用户无法修改存储过程,可以尝试在MyBatis中捕获异常并打印详细的错误信息,以便进一步分析。 在MyBatis中捕获异常: ```java try { mapper.callBuildStatD(sqlDate); } catch (Exception e) { // 如果是PersistenceException,可以获取更详细的SQL错误 if (e instanceof PersistenceException) { PersistenceException pe = (PersistenceException) e; Throwable cause = pe.getCause(); if (cause instanceof SQLException) { SQLException sqlEx = (SQLException) cause; while (sqlEx != null) { System.out.println("SQLState: " + sqlEx.getSQLState()); System.out.println("Error Code: " + sqlEx.getErrorCode()); System.out.println("Message: " + sqlEx.getMessage()); sqlEx = sqlEx.getNextException(); } } } } ``` 通过上述步骤,我们可以逐步定位问题。 相关问题: 1. 如何在Java中获取昨天的日期? 2. 如何将Java的LocalDate转换为java.sql.Date? 3. 如何在PostgreSQL中直接调用存储过程进行调试? 4. 如何捕获MyBatis执行过程中的SQL异常? 5. 如何为数据库用户授予存储过程的执行权限?</think>### MyBatis调用PostgreSQL存储过程报错排查指南 针对您调用存储过程 `opr_stat.proc_table_spark_build_stat_d(date)` 时传递参数 'yesterday' 报错的问题,以下是系统排查方案: #### 核心问题分析 1. **参数类型不匹配** PostgreSQL要求严格的日期类型,'yesterday' 是字符串而非有效日期格式 2. **存储过程内部错误** 报错指向第43行SQL语句,可能因参数格式错误导致SQL执行异常 3. **调用方式差异** MyBatis调用方式与直接SQL调用存在语法差异[^1] #### 解决方案 ##### 第一步:修正日期参数格式 在Java代码中动态计算昨天日期: ```java // 使用Java 8+日期API LocalDate yesterday = LocalDate.now().minusDays(1); // MyBatis参数传递 Map<String, Object> params = new HashMap<>(); params.put("statDate", java.sql.Date.valueOf(yesterday)); // 必须转为java.sql.Date // Mapper调用 statMapper.callBuildStatD(params); ``` ##### 第二步:修改MyBatis调用语法 在Mapper XML中使用正确语法: ```xml <!-- 方案1:函数调用语法(推荐) --> <select id="callBuildStatD" resultType="map" statementType="CALLABLE"> SELECT opr_stat.proc_table_spark_build_stat_d( #{statDate, jdbcType=DATE, mode=IN} ) </select> <!-- 方案2:CALL语法(PG11+) --> <select id="callBuildStatD" statementType="CALLABLE"> CALL opr_stat.proc_table_spark_build_stat_d( #{statDate, jdbcType=DATE} ) </select> ``` ##### 第三步:存储过程调试 在存储过程中添加日志定位第43行错误: ```sql CREATE OR REPLACE PROCEDURE opr_stat.proc_table_spark_build_stat_d(stat_date DATE) LANGUAGE plpgsql AS $$ DECLARE v_sql TEXT; BEGIN RAISE NOTICE 'Received date: %', stat_date; -- 确认参数接收 -- 原第43行SQL(示例) v_sql := format('INSERT INTO stat_table SELECT ... WHERE date_col = %L', stat_date); RAISE NOTICE 'Executing SQL: %', v_sql; -- 打印实际执行的SQL EXECUTE v_sql; -- 原第43行位置 EXCEPTION WHEN OTHERS THEN RAISE EXCEPTION 'Line43 Error: %', SQLERRM; END; $$; ``` ##### 第四步:MyBatis错误捕获 获取详细错误信息: ```java try { statMapper.callBuildStatD(params); } catch (PersistenceException e) { SQLException sqlEx = (SQLException) e.getCause(); // 提取PostgreSQL原生错误码 String sqlState = sqlEx.getSQLState(); // 如: P0001, 42883 String errorMsg = sqlEx.getMessage(); log.error("PostgreSQL Error [{}]: {}", sqlState, errorMsg); // 根据错误码精准处理 if ("42883".equals(sqlState)) { log.error("函数不存在或参数不匹配,请检查存储过程签名"); } } ``` #### 常见错误对照表 | 错误现象 | 原因分析 | 解决方案 | |--------------------------|--------------------------|-----------------------------| | `function does not exist` | 参数类型不匹配[^3] | 使用`java.sql.Date`类型 | | `invalid input syntax` | 'yesterday'不是有效日期 | Java计算实际日期值 | | `column "xxx" not exist` | 动态SQL拼接错误 | 存储过程内打印SQL语句 | | `permission denied` | 用户无执行权限 | `GRANT EXECUTE ON PROCEDURE...` | #### 验证方案 1. **参数有效性测试** ```sql -- 直接测试存储过程 SELECT opr_stat.proc_table_spark_build_stat_d(CURRENT_DATE - INTERVAL '1 DAY'); ``` 2. **权限检查** ```sql -- 查看存储过程权限 SELECT proname, procostype FROM pg_proc WHERE proname = 'proc_table_spark_build_stat_d'; ``` 3. **MyBatis SQL日志** 在`mybatis-config.xml`启用日志: ```xml <configuration> <settings> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> </configuration> ``` 检查实际执行的SQL: ```log ==> Preparing: SELECT opr_stat.proc_table_spark_build_stat_d(?) ==> Parameters: 2023-06-19(Date) ``` #### 最佳实践建议 1. 使用显式类型转换: ```sql -- 在存储过程中添加类型转换 CREATE OR REPLACE PROCEDURE opr_stat.proc_table_spark_build_stat_d(stat_date VARCHAR) AS $$ BEGIN PERFORM opr_stat.proc_table_spark_build_stat_d( CASE stat_date WHEN 'yesterday' THEN CURRENT_DATE - 1 ELSE stat_date::DATE END ); END; $$; ``` 2. 参数验证机制: ```java // Spring验证注解 public void callProcedure( @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate statDate) { // 调用Mapper } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值