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

在Oracle ADF的Java代码中动态添加键值对并调用存储过程的最优方案

最优实现方案

整体思路

  1. 数据库端定义嵌套表类型接收多组键值对数据,编写对应批量处理的存储过程
  2. Java端将选中行数据转换为对应数据结构,批量封装后传入存储过程
  3. 通过ADF获取数据库连接,调用存储过程完成批量操作,同时处理事务与异常

步骤1:数据库端定义类型与存储过程

假设使用Oracle数据库,先定义单行数据类型和嵌套表类型,再编写批量处理的存储过程:

-- 定义单行审批数据类型
CREATE OR REPLACE TYPE REC_APPROVAL_DATA AS OBJECT (
    ID NUMBER,
    START_DATE TIMESTAMP,
    SRC_TYPE VARCHAR2(10),
    TYPE VARCHAR2(10),
    UPDATED_BY VARCHAR2(50),
    LAST_UPDATE_DATE DATE
);

-- 定义批量数据的嵌套表类型
CREATE OR REPLACE TYPE TBL_APPROVAL_DATA AS TABLE OF REC_APPROVAL_DATA;

-- 批量审批存储过程
CREATE OR REPLACE PROCEDURE PROC_BATCH_APPROVE(p_data TBL_APPROVAL_DATA)
IS
BEGIN
    -- 用FORALL实现高效批量更新(比循环单条更新性能提升数倍)
    FORALL i IN p_data.FIRST..p_data.LAST
        UPDATE YOUR_TARGET_TABLE
        SET TYPE = p_data(i).TYPE,
            UPDATED_BY = p_data(i).UPDATED_BY,
            LAST_UPDATE_DATE = p_data(i).LAST_UPDATE_DATE
        WHERE ID = p_data(i).ID
          AND SRC_TYPE = p_data(i).SRC_TYPE;
    COMMIT;
END;
/

步骤2:Java端改造代码

1. 定义DTO映射数据库单行类型

创建简单Java类对应数据库的REC_APPROVAL_DATA类型,方便数据封装:

public class ApprovalData {
    private Long id;
    private Timestamp startDate;
    private String srcType;
    private String type;
    private String updatedBy;
    private Date lastUpdateDate;

    // 构造方法
    public ApprovalData(Long id, Timestamp startDate, String srcType, String type, String updatedBy, Date lastUpdateDate) {
        this.id = id;
        this.startDate = startDate;
        this.srcType = srcType;
        this.type = type;
        this.updatedBy = updatedBy;
        this.lastUpdateDate = lastUpdateDate;
    }

    // 省略getter/setter方法
}

2. 改造原业务代码,收集数据并调用存储过程

ViewObject VO = getViewObjectFromAMImpl("EOView2", "AppModuleDataControl");
Row[] selectedRows = VO.getFilteredRows("tSelect", true);
List<ApprovalData> approvalList = new ArrayList<>();

ADFContext adfCtx = ADFContext.getCurrent();
SecurityContext secCntx = adfCtx.getSecurityContext();
String currentUser = secCntx.getUserName();
Date currentDate = new Date();

// 筛选符合条件的行,封装到列表
for (Row r : selectedRows) {
    if ("E".equals(r.getAttribute("SrcType"))) {
        Long id = (Long) r.getAttribute("Id"); // 替换为实际主键字段
        Timestamp startDate = (java.sql.Timestamp) r.getAttribute("StartDate");
        String srcType = (String) r.getAttribute("SrcType");
        
        ApprovalData data = new ApprovalData(id, startDate, srcType, "S", currentUser, currentDate);
        approvalList.add(data);
    }
}

int counter = approvalList.size();
System.out.println("printing count" + counter);

if (counter == 0) {
    JSFUtils.addFacesErrorMessage("No records Approved.");
} else {
    // 调用批量存储过程
    callBatchApprovalProcedure(approvalList);
    JSFUtils.addFacesInformationMessage(counter + " records Approved successfully.");
    AdfFacesContext.getCurrentInstance().addPartialTarget(hearderTableBind);
}
approvePopup.cancel();

3. 实现存储过程调用方法

private void callBatchApprovalProcedure(List<ApprovalData> approvalList) {
    Connection conn = null;
    CallableStatement stmt = null;
    try {
        // 获取ADF事务的数据库连接
        conn = getDBTransaction().getConnection();
        
        // 注册数据库定义的嵌套表和单行类型
        ArrayDescriptor arrayDesc = ArrayDescriptor.createDescriptor("TBL_APPROVAL_DATA", conn);
        StructDescriptor structDesc = StructDescriptor.createDescriptor("REC_APPROVAL_DATA", conn);
        
        // 将List转换为数据库可识别的Struct数组
        Struct[] structArray = new Struct[approvalList.size()];
        for (int i = 0; i < approvalList.size(); i++) {
            ApprovalData data = approvalList.get(i);
            Object[] structAttrs = new Object[]{
                data.getId(),
                data.getStartDate(),
                data.getSrcType(),
                data.getType(),
                data.getUpdatedBy(),
                data.getLastUpdateDate()
            };
            structArray[i] = new Struct(structDesc, conn, structAttrs);
        }
        
        // 封装为ARRAY对象传入存储过程
        ARRAY dataArray = new ARRAY(arrayDesc, conn, structArray);
        
        // 执行存储过程
        String sql = "{call PROC_BATCH_APPROVE(?)}";
        stmt = conn.prepareCall(sql);
        stmt.setArray(1, dataArray);
        stmt.execute();
        
        // 提交事务(若存储过程未内部提交)
        getDBTransaction().commit();
        
    } catch (SQLException e) {
        // 异常回滚,提示错误
        getDBTransaction().rollback();
        JSFUtils.addFacesErrorMessage("Approval failed: " + e.getMessage());
        e.printStackTrace();
    } finally {
        // 关闭资源
        try {
            if (stmt != null) stmt.close();
            if (conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

关键注意事项

  • 数据库类型名称需与代码中完全一致(Oracle默认区分大小写,建议统一用大写)
  • 若使用JDeveloper开发,可通过向导自动生成数据库类型对应的Java类,无需手动处理Struct和ARRAY
  • 存储过程中使用FORALL而非循环单条操作,是批量处理的性能核心
  • 异常处理需确保事务一致性,避免部分数据更新成功的情况

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 09:02:54