iBatis(现在更名为myBatis了)可以被看做是一个"半自动化"的ORM工具(通过sql-mapper),或者更严格的说,是一个关于JDBC API的强大封装。简单易学是它的优点,这也就是为什么在很多项目中,我都倾向于使用iBatis,尤其是提供大量复杂查询或者报表功能的应用当中。
在<<iBatis in Action>>这本书中,讲解了关于iBatis的各种用法,但是作者也特别强调了:在Dataaccess这层上,通过调用存储过程是一个反模式,不推荐使用;于是关于通过iBatis调用存储过程,只是非常简单的一笔带过。
但是我们也通常会在很多场合中,遇到要使用存储过程的情况。就存储过程本身,以Oracle存储过程为例,我们完全可以在一个jdbc事务里面,完成有多个前后依赖步骤的较为复杂的数据库访问,从而减少应用与数据库之间的往返通讯。
在这里,以Oracle数据库为例,讲述通过iBatis调用Oracle存储过程时的配置思路。在这里我假定阅读此文的人有关于iBatis的使用经验,从而可以略过一些细节描述。
先看一下Oracle存储过程接口:
对应地,在ibatis的xml配置文件中,针对该存储过程调用的配置如下所述:
上面这段代码包含了两点,id为“map_r_process_order”声明了传入参数和Oracle存储过程接口参数之间的映射关系,按照顺序对应;
id为"r_process_order"执行存储过程调用。存储过程的IN参数类型和OUT参数类型,总数为13个,所以?号个数也是13,顺序对应。
Java代码在此处从略:基本上,较为简单的做法,就是针对IN和OUT类型,准备两个VO对象,分别包装存储过程的传入参数和传出参数。
传入参数属性结构较为简单。
传出参数,既包括基本类型例如flag(integer),msg(String),也包括了复合类型。在这里orderItemList表示我们期望存储过程以游标返回一个 List<OrderItem>结果集,指定jdbcType为ORACLECURSOR,iBatis会自动帮助我们完成ResultSet与POJO的数据绑定(这里的POJO是 OrderItem)。当然,在这里我们需要提供一个resultMap,来描述游标行集中的每一个行,与我们的OrderItem之间的映射关系。
在<<iBatis in Action>>这本书中,讲解了关于iBatis的各种用法,但是作者也特别强调了:在Dataaccess这层上,通过调用存储过程是一个反模式,不推荐使用;于是关于通过iBatis调用存储过程,只是非常简单的一笔带过。
但是我们也通常会在很多场合中,遇到要使用存储过程的情况。就存储过程本身,以Oracle存储过程为例,我们完全可以在一个jdbc事务里面,完成有多个前后依赖步骤的较为复杂的数据库访问,从而减少应用与数据库之间的往返通讯。
在这里,以Oracle数据库为例,讲述通过iBatis调用Oracle存储过程时的配置思路。在这里我假定阅读此文的人有关于iBatis的使用经验,从而可以略过一些细节描述。
先看一下Oracle存储过程接口:
PROCEDURE r_process_order(is_oid IN VARCHAR2, -- 订单号
is_taskid in varchar2, --任务单号
is_price in number, --价格
is_gap in number, --浮动价差
is_notes in varchar2, --备注
is_timelimit in number, --报价有效时间限制
is_userid in varchar2, -- 用户编号
is_deptid in varchar2, -- 用户所在部门id
oi_flag OUT INTEGER, --0 成功 -1 错误
os_msg OUT VARCHAR2, --出错信息
os_finish out varchar2, --是否完成订单流程 0否 1是
or_order_item_list out sys_refcursor, -- 订单条目信息
or_task_item_list out sys_refcursor, -- 后续待办事项信息
or_userlist out sys_refcursor -- 广播通知的用户集
);
对应地,在ibatis的xml配置文件中,针对该存储过程调用的配置如下所述:
<!-- 订单:应答 -->
<parameterMap id="map_r_process_order" class="doRfqReply">
<parameter property="orderId" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN" />
<parameter property="taskId" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN" />
<parameter property="price" jdbcType="DOUBLE" javaType="java.math.BigDecimal" mode="IN" />
<parameter property="gap" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN" />
<parameter property="notes" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN" />
<parameter property="userId" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN" />
<parameter property="deptId" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN" />
<parameter property="flag" jdbcType="INTEGER" javaType="int" mode="OUT" />
<parameter property="msg" jdbcType="VARCHAR" javaType="java.lang.String" mode="OUT" />
<parameter property="strFinish" jdbcType="VARCHAR" javaType="java.lang.String" mode="OUT" />
<parameter property="orderItemList" jdbcType="ORACLECURSOR" javaType="java.sql.ResultSet" mode="OUT"
resultMap="orderItemListResult" />
<parameter property="taskItemList" jdbcType="ORACLECURSOR" javaType="java.sql.ResultSet" mode="OUT"
resultMap="taskItemListResult" />
<parameter property="processUserIds" jdbcType="ORACLECURSOR" javaType="java.sql.ResultSet" mode="OUT"
resultMap="processUserId" />
</parameterMap>
<procedure id="r_process_order" parameterMap="map_r_rfq_reply">
{ call p_foo_bar_manager.r_process_order(?,?,?,?,?,?,?,?,?,?,?,?,?)}
</procedure>
上面这段代码包含了两点,id为“map_r_process_order”声明了传入参数和Oracle存储过程接口参数之间的映射关系,按照顺序对应;
id为"r_process_order"执行存储过程调用。存储过程的IN参数类型和OUT参数类型,总数为13个,所以?号个数也是13,顺序对应。
Java代码在此处从略:基本上,较为简单的做法,就是针对IN和OUT类型,准备两个VO对象,分别包装存储过程的传入参数和传出参数。
传入参数属性结构较为简单。
传出参数,既包括基本类型例如flag(integer),msg(String),也包括了复合类型。在这里orderItemList表示我们期望存储过程以游标返回一个 List<OrderItem>结果集,指定jdbcType为ORACLECURSOR,iBatis会自动帮助我们完成ResultSet与POJO的数据绑定(这里的POJO是 OrderItem)。当然,在这里我们需要提供一个resultMap,来描述游标行集中的每一个行,与我们的OrderItem之间的映射关系。