如何使用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
相关产品推荐
相关产品推荐

