You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JPA和Hibernate调用Oracle存储过程与函数并返回单个实体

问题根因

你调用PL/SQL函数得到的CLOB内容本身就是数组格式的JSON,之前用getSingleResult()没有达到预期,是因为该方法只负责从数据库查询结果集中取出唯一的一行数据,而你这行数据存储的本身就是完整的JSON数组字符串,它不会自动解析JSON内容提取单个对象。

解决方案

分两种场景处理:

场景1:可修改PL/SQL函数CUST_INFO_COURT.GET_ACCOUNT_INFO

直接调整函数逻辑,让它只返回你需要的单个对象结构的JSON,不需要外层包裹数组,这是成本最低的方案。调整后Java代码只要稍微优化即可,同时修复原有SQL拼接导致的注入风险:

@Override
public String getAccountInfo(String pinCode, String voen) throws SQLException {
    // 使用参数绑定替代字符串拼接,避免SQL注入
    Query q = em.createNativeQuery("select CUST_INFO_COURT.GET_ACCOUNT_INFO@DWHX(?1, ?2) from dual");
    q.setParameter(1, pinCode);
    q.setParameter(2, voen);
    // 取唯一行的Clob结果
    Clob clob = (Clob) q.getSingleResult();
    if (clob == null) {
        return "Data Not Found.";
    }
    return clob.getSubString(1, (int) clob.length());
}

场景2:不可修改PL/SQL函数

需要在Java侧解析返回的JSON数组,取出第一个元素再返回,推荐用Jackson等JSON序列化工具处理:

方案2.1 直接返回单个对象的JSON字符串

// 提前初始化Jackson的ObjectMapper,Spring项目可直接@Autowired注入
private final ObjectMapper objectMapper = new ObjectMapper();

@Override
public String getAccountInfo(String pinCode, String voen) throws Exception {
    Query q = em.createNativeQuery("select CUST_INFO_COURT.GET_ACCOUNT_INFO@DWHX(?1, ?2) from dual");
    q.setParameter(1, pinCode);
    q.setParameter(2, voen);
    Clob clob = (Clob) q.getSingleResult();
    if (clob == null) {
        return "Data Not Found.";
    }
    String jsonArrayStr = clob.getSubString(1, (int) clob.length());
    JsonNode jsonNode = objectMapper.readTree(jsonArrayStr);
    // 判断是数组且有值则取第一个元素返回
    if (jsonNode.isArray() && jsonNode.size() > 0) {
        return objectMapper.writeValueAsString(jsonNode.get(0));
    }
    return "Data Not Found.";
}

方案2.2 直接返回Java实体对象

首先定义对应实体类:

// 省略get/set方法,可使用Lombok的@Data注解自动生成
public class AccountInfo {
    private String account_number;
    private String account_type;
    private String open_date;
    private String currency;
    private BigDecimal balance;
    private String closure_date;
    private String status;
}

修改业务方法:

private final ObjectMapper objectMapper = new ObjectMapper();

@Override
public AccountInfo getAccountInfo(String pinCode, String voen) throws Exception {
    Query q = em.createNativeQuery("select CUST_INFO_COURT.GET_ACCOUNT_INFO@DWHX(?1, ?2) from dual");
    q.setParameter(1, pinCode);
    q.setParameter(2, voen);
    Clob clob = (Clob) q.getSingleResult();
    if (clob == null) {
        return null;
    }
    String jsonArrayStr = clob.getSubString(1, (int) clob.length());
    // 解析为实体列表
    List<AccountInfo> accountList = objectMapper.readValue(jsonArrayStr, new TypeReference<List<AccountInfo>>() {});
    // 取第一个元素返回
    return accountList.stream().findFirst().orElse(null);
}

内容的提问来源于stack exchange,提问作者iwritecomeinmymind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 07:45:05