MyBatis调用自定义类型存储过程

MyBatis调用自定义类型存储过程

因业务及性能需要,有时会将一些复杂的数据查询场景通过数据库存储过程实现,通过Java+Mybatis实现调用。本文档描述了这种方案的基本使用。其中使用了自定义类型、自定义类型数组作为入参,且能返回自定义类型数组数据给调用方。

以下以Oracle数据库为例,做以说明。

数据库对象创建

自定义对象类型

-- 事件数据
type EVENT_DATA as object
(
  ID      number,
  TERMINAL_ID varchar2(10),
  VIN  varchar2(17),
  ICID varchar2(7),
  VEHICLESTATE number,
  LAT number,
  LNG number,
  MILEAGE number,
  DATATIME timestamp,
  SAVETIME timestamp,
  STATE number,
  DO_UPDATE number,
  SALE_ID number,
  CAR_STATE number
);

-- 事件参数
type EVENT_PARAM as object (
  saleId number(10),
  vin varchar2(17),
  seq number(10),
  status number(1),
  time varchar2(20),
  doUpdate number(1),
  carState number(1),
  icCard varchar2(20)
);
复制代码

自定义对象数组类型

-- 事件数据数组
type EVENT_DATA_ARR as table of EVENT_DATA;
-- 事件参数数组
type EVENT_PARAM_ARR as table of EVENT_PARAM

复制代码

自定义存储过程


-- 拼接查询sql
CREATE OR REPLACE procedure query_iccard_sql(vinParam in varchar2, seqId in number, dateTime in timestamp, out_return out SYS_REFCURSOR) is
  str clob;
  
begin
    -- 拼接sql
    str:= 'select ID,
                  TERMINAL_ID,
                  VIN,
                  ICID,
                  VEHICLESTATE,
                  LAT,LNG,
                  MILEAGE,
                  DATATIME,
                  SAVETIME,
                  :seqId
    from t_s_iccardevent e
    where e.vin =:vinParam
    and not exists (select 1 from beibos.t_b_Lifecycle_Aftersale a where a.vin=:vinParam
    and e.id = a.SRC_START_SEQ and e.id != a.SRC_END_SEQ)
    and vehiclestate in (3, 4) and e.STATE = 1 ';
    
    -- 追加时间条件
    if dateTime is not null then
      str := str || ' and e.DATATIME >= :dateTime and e.id > :seqId order by e.id asc ';
      dbms_output.put_line('sql = ' || str);
      open out_return for str using seqId, vinParam, vinParam, dateTime, seqId;
    else
      str := str || ' and e.id > :seqId order by e.id asc ';
      dbms_output.put_line('sql = ' || str);
      dbms_output.put_line('');
      open out_return for str using seqId, vinParam, vinParam, seqId;
    end if;
end;


-- 查询事件数据

CREATE OR REPLACE procedure query_iccard_event(i_event_params in event_param_arr, o_event_datas out event_data_arr) is
  -- 存储生成的sql游标
  cur1 SYS_REFCURSOR;
  -- IC卡事件数据存储对象
  iccardevent t_s_iccardevent%rowtype;
  -- 计数器
  count_num number;
  -- 数组索引
  i number;
  --j number;
  -- 事件数据
  ed event_data;
  -- 事件数据集合
  edArr event_data_arr := event_data_arr();
  
  --filterSrcEvent event_data_arr := event_data_arr();
begin
  -- 游标索引初始化
  i:= 1;
  --j:= 1;
  -- 计数器初始化
  count_num:=1;
  FOR i in 1..i_event_params.count LOOP
    BEGIN

      query_iccard_sql(i_event_params(i).vin, i_event_params(i).seq, 
                                               to_date(i_event_params(i).time, 'yyyy-MM-dd hh24:mi:ss'), cur1);
      loop
        fetch cur1
          into iccardevent;
          exit when cur1%notfound;
            edArr.extend;
            ed := event_data(null, null, null, null, null, null, null, null, null, null, null, null, null, null);
            ed.id := iccardevent.id;
            ed.terminal_id := iccardevent.TERMINAL_ID;
            ed.vin := iccardevent.VIN;
            ed.icid := iccardevent.ICID;
            ed.vehiclestate := iccardevent.VEHICLESTATE;
            ed.lat := iccardevent.LAT;
            ed.lng := iccardevent.LNG;
            ed.mileage := iccardevent.MILEAGE;
            ed.datatime := iccardevent.DATATIME;
            ed.savetime := iccardevent.SAVETIME;
            ed.state := iccardevent.state;
            ed.do_update := i_event_params(i).doUpdate;
            ed.sale_id := i_event_params(i).saleId;
            ed.car_state := i_event_params(i).carState;
            
            edArr(count_num) := ed;
            count_num := count_num+1;
        end loop;
      close cur1;

    END;
  END LOOP;
  
  o_event_datas:=edArr;
end;

-- 测试调用
declare
  ep    event_param := event_param(null, null, null, null, null, null, null, null);
  ep2   event_param := event_param(null, null, null, null, null, null, null, null); 
  epArr event_param_arr := event_param_arr();
  edArr event_data_arr := event_data_arr();
  iq    number;
begin
  epArr.extend(2);
  ep.vin := 'LBZ44DEB2HA012563';
  ep.seq := 11945;
  ep.status := 4;
  ep.time := '2018-04-19 10:51:07';
  ep.doupdate := 1;
  ep.carState := 3;
  ep.saleId := 130131;
  ep.icCard := null;
  epArr(1) := ep;
  
  ep2.vin := 'LBZ44DEB2HA012563';
  ep2.seq := 11925;
  ep2.status := 4;
  ep2.time := '2018-04-19 09:57:27';
  ep2.doupdate := 1;
  ep2.carState := 3;
  ep2.saleId := 130138;
  ep2.icCard := null;
  epArr(2) := ep2;
  
  query_iccard_event(epArr, edArr);
  
  iq := 1;
  for iq in 1 .. edArr.count LOOP
    dbms_output.put_line('ID = ' || edArr(iq).id 
                                      || ', TERMINAL_ID = '
                                      || edArr(iq).terminal_id
                                      || ', VIN = ' 
                                      || edArr(iq).vin 
                                      || ', ICID = ' 
                                      || edArr(iq).icid 
                                      || ', VEHICLESTATE = ' 
                                      || edArr(iq).vehiclestate
                                      || ', LAT = '
                                      || edArr(iq).lat
                                      || ', LNG = '
                                      || edArr(iq).lng
                                      || ', MILEAGE = '
                                      || edArr(iq).MILEAGE
                                      || ', DATATIME = '
                                      || to_char(edArr(iq).DATATIME, 'yyyy-MM-dd HH:mi:ss')
                                      || ', SAVETIME = '
                                      || to_char(edArr(iq).SAVETIME, 'yyyy-MM-dd HH:mi:ss')
                                      || ', STATE = '
                                      || edArr(iq).STATE
                                      || ', DO_UPDATE = ' 
                                      || edArr(iq).DO_UPDATE
                                      || ', SALE_ID = ' 
                                      || edArr(iq).SALE_ID
                                      || ', CAR_STATE = ' 
                                      || edArr(iq).CAR_STATE);
  END LOOP;

end;
/
复制代码

数据库对象创建部分到此已经完成。

Java代码部分

自定义类型解析器

事件查询参数数组解析器

用于处理事件查询参数映射。重点关注setParameter方法实现。

@MappedJdbcTypes(JdbcType.ARRAY)
public class EventParamArrTypeHandler implements TypeHandler {

    @Override
    public void setParameter(PreparedStatement ps, int i, Object paramsObj, JdbcType jdbcType) throws SQLException {
        Connection conn = null;
        try {
            ArrayList<EventParam> params = (ArrayList<EventParam>)paramsObj;
            if(CollectionUtils.isNotEmpty(params)) {
                DataSource dataSource =  SpringContextUtil.getBean("masterDataSource");
                if(dataSource != null) {
                    conn = dataSource.getConnection();
                }
                if(null != conn) {
                    conn = conn.unwrap(OracleConnection.class);
                    STRUCT[] structArr = new STRUCT[params.size()];
                    for(int j = 0; j < params.size(); j++) {
                        EventParam param = params.get(j);
                        // EVENT_PARAM 为数据库自定义类型名称,需大写
                        StructDescriptor sd = StructDescriptor .createDescriptor("EVENT_PARAM", conn);
                        Object[] o = new Object[8];
                        o[0] = param.getSaleId();
                        o[1] = param.getVin();
                        o[2] = param.getSeq();
                        o[3] = param.getStatus();
                        o[4] = param.getTime();
                        o[5] = param.getDoUpdate();
                        o[6] = param.getCarState();
                        o[7] = param.getIcCard();
                        structArr[j] = new STRUCT(sd, conn, o);
                    }
                    // EVENT_PARAM_ARR 为数据库自定义数组类型名称,需大写
                    ArrayDescriptor arrayDes = ArrayDescriptor.createDescriptor("EVENT_PARAM_ARR", conn);
                    ARRAY array = new ARRAY(arrayDes, conn, structArr);
                    ps.setArray(i, array);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            if(null != conn){
                conn.close();
            }
        }
    }

    @Override
    public Object getResult(ResultSet resultSet, String s) throws SQLException {
        return null;
    }

    @Override
    public Object getResult(ResultSet resultSet, int i) throws SQLException {
        return null;
    }

    @Override
    public Object getResult(CallableStatement callableStatement, int i) throws SQLException {
        return null;
    }
}
复制代码

事件数据数组解析器

用于处理存储过程返回值业务。重点关注getResult方法实现。

@MappedJdbcTypes(JdbcType.ARRAY)
public class EventDataArrTypeHandler implements TypeHandler {

    @Override
    public void setParameter(PreparedStatement ps, int i, Object obj, JdbcType jdbcType) throws SQLException {
    }

    @Override
    public Object getResult(ResultSet resultSet, String s) throws SQLException {
        return null;
    }

    @Override
    public Object getResult(ResultSet resultSet, int i) throws SQLException {
        return null;
    }

    @Override
    public Object getResult(CallableStatement cs, int i) throws SQLException {
        Connection conn = null;
        List<IcCardEvent> objList =  new ArrayList<>();
        try {
            conn = cs.getConnection();
            if(null != conn) {
                ResultSet rs = cs.getArray(i).getResultSet();
                while (rs.next()) {
                    Datum[] d = ((STRUCT)rs.getObject(2)).getOracleAttributes();
                    IcCardEvent event = new IcCardEvent();
                    event.setId(d[0].longValue());
                    event.setTerminalId(d[1].stringValue());
                    event.setVin(d[2].stringValue());
                    event.setIcid(d[3].stringValue());
                    event.setVehiclestate(d[4].intValue());
                    event.setLat(d[5].longValue());
                    event.setLng(d[6].longValue());
                    event.setMileage(d[7].longValue());
                    event.setDatatime(d[8].dateValue());
                    event.setSavetime(d[9].dateValue());
                    event.setSaleId(d[10].intValue());
                    event.setDoUpdate(d[11].intValue());
                    event.setCarState(d[12].intValue());
                    objList.add(event);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            if(null != conn){
                conn.close();
            }
        }
        return objList;
    }
}
复制代码

Mapper接口声明

Mapper接口声明。

List<IcCardEvent> queryEventByParams(@Param("map") Map<String, Object> map);
复制代码

Mapper XML配置

Mapper SQL映射部分。 需通过typeHandler属性配置具体的解析器。jdbcType类型使用ARRAY

<select id="queryEventByParams" statementType="CALLABLE" resultMap="BaseResultMap">
  {
     call query_iccard_event(
        #{map.params,mode=IN,jdbcType=ARRAY,typeHandler=com.hopechart.micro.typehandler.EventParamArrTypeHandler},
        #{map.events,mode=OUT,jdbcType=ARRAY, jdbcTypeName="EVENT_DATA_ARR", typeHandler=com.hopechart.micro.typehandler.EventDataArrTypeHandler}
     )
  }
</select>
复制代码

业务接口调用

public List<IcCardEvent> queryEventByParams(List<EventParam> params) throws BusinessException {
    try {
        List<IcCardEvent> events = new ArrayList<>();
        Map<String, Object> map = new HashMap();
        map.put("params", params);
        map.put("events", events);
        // 调用
        icCardEventMapper.queryEventByParams(map);
        events = (List<IcCardEvent>) map.get("events");
            
         return events;
    } catch (Exception e) {
        LOGGER.error("查询原始IC卡时间数据异常", e);
        throw e;
    }
}
复制代码

至此,在Java中通过Mybatis调用自定义存储过程,实现自定义类型数组输入、自定义类型数组输出的方法已经阐述完毕。 希望此文档对有需要的同学能带来帮助,如有不妥之处还请指正,谢谢!

作者:代江

转载于:https://juejin.im/post/5cdcd64fe51d456e6154b53a

  • 1
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值