使用JPA调用Oracle存储过程时出现错误
嘿,我看你正在用JPA EntityManager调用Oracle存储过程拿指定日期范围的关联订单列表,数据库端测过没问题,但代码好像没写完对吧?我来帮你补全代码,再唠唠我踩过的几个坑~
完整的JPA调用Oracle存储过程实现
先把你的代码补全,再拆解每个部分的注意点:
1. 补全后的调用代码
假设你的存储过程Get_submitted_orders需要两个输入参数(起始日期、结束日期),输出一个REF_CURSOR返回订单数据,完整代码应该是这样:
// 创建存储过程查询实例 StoredProcedureQuery query = entityManager.createStoredProcedureQuery("Get_submitted_orders") // 第一个参数:输出的REF_CURSOR,别用Class.class,这里用void.class就行,或者直接指定实体类 .registerStoredProcedureParameter(1, void.class, ParameterMode.REF_CURSOR) // 第二个参数:起始日期(如果存储过程接受DATE类型,建议用Date而非String) .registerStoredProcedureParameter(2, String.class, ParameterMode.IN) // 第三个参数:结束日期 .registerStoredProcedureParameter(3, String.class, ParameterMode.IN); // 设置输入的日期参数 query.setParameter(2, "2024-01-01"); query.setParameter(3, "2024-06-30"); // 执行存储过程 query.execute(); // 获取结果集:如果没有对应实体,就用Object[]接收,再转成DTO List<Object[]> rawResults = query.getResultList(); List<OrderDTO> orderList = new ArrayList<>(); for (Object[] row : rawResults) { OrderDTO dto = new OrderDTO(); dto.setOrderId((Long) row[0]); dto.setOrderNo((String) row[1]); dto.setSubmitTime((Timestamp) row[2]); // 按存储过程返回的列顺序,挨个赋值其他字段 orderList.add(dto); }
2. 容易踩的几个坑
- REF_CURSOR注册错了:很多人会写成
registerStoredProcedureParameter(1, Class.class, ParameterMode.REF_CURSOR),这是错的!正确的应该用void.class,如果你的实体和存储过程返回的列完全匹配,也可以直接写实体类(比如Order.class),这样JPA会自动帮你映射成实体列表,不用手动转DTO。 - 日期参数类型不匹配:如果存储过程的参数是Oracle的
DATE类型,别传String!直接传java.util.Date或者java.sql.Timestamp,避免格式转换出错,比如:.registerStoredProcedureParameter(2, Date.class, ParameterMode.IN) query.setParameter(2, LocalDate.of(2024,1,1).atStartOfDay().toInstant().atZone(ZoneId.systemDefault()).toDate()); - 结果集映射乱了:存储过程返回的游标列顺序和类型必须和你接收的对象完全对应,比如游标第一列是
ORDER_ID(数字类型),那你用row[0]转成Long就对了,要是顺序错了或者类型强转错了,直接抛异常。 - 权限问题:别以为数据库端能跑通就万事大吉,应用端的数据库账号得有执行这个存储过程的权限,不然会报权限不足的错误。
3. 更简洁的写法:用@NamedStoredProcedureQuery
如果你经常调用这个存储过程,可以在实体上注解配置,下次调用就不用重复注册参数了:
@Entity @NamedStoredProcedureQuery( name = "Order.getSubmittedOrders", procedureName = "Get_submitted_orders", parameters = { @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class), @StoredProcedureParameter(mode = ParameterMode.IN, type = String.class), @StoredProcedureParameter(mode = ParameterMode.IN, type = String.class) }, resultClasses = Order.class // 直接指定实体类,自动映射 ) public class Order { // 你的实体字段,注意和存储过程返回的列名对应(比如orderId对应ORDER_ID) }
调用的时候就简单多了:
StoredProcedureQuery query = entityManager.createNamedStoredProcedureQuery("Order.getSubmittedOrders"); query.setParameter(2, "2024-01-01"); query.setParameter(3, "2024-06-30"); List<Order> orderList = query.getResultList();
内容的提问来源于stack exchange,提问作者Krish
相关产品推荐
相关产品推荐

