mybatis 存储过程与游标的使用
MyBatis还能对存储过程进行完全支持,这节开始学习存储过程。在讲解之前,我们需要对存储过程有一个基本的认识,首先存储过程是数据库的一个概念,它是数据库预先编译好,放在数据库内存中的一个程序片段,所以具备性能高,可重复使用的特性。它定义了3种类型的参数:输入参数、输出参数、输入输出参数。
•输入参数,是外界给的存储过程参数,在Java互联网中,也就是互联网系统给它的参数。
•输出参数,是存储过程经过计算返回给程序的结果参数。
•输入输出参数,是一开始作为参数传递给存储过程,而存储过程修改后将其返回的参数,比如那些商品的库存就是这样的。
对于返回结果而言,一些常用的简易类型,比如整形、字符型OUT或者INOUT参数是Java程序比较好处理的,而存储过程还可能返回游标类型的参数,这需要我们处理,不过在MyBatis中,这些都可以轻松完成。
IN和OUT参数存储过程
先讨论IN和OUT参数的基本用法,这里使用的是Oracle数据库,它对存储过程有着较好的支持,下面先定义一个场景。
根据角色名称进行模糊查询其总数,然后把总数和查询日期返回给调用者。为此先建一个简单的存储过程,在Oracle的命令行输入存储过程,如代码清单的代码。
CREATE OR REPLACE PROCEDURE count_role ( p_role_name IN VARCHAR, count_total out INT, exec_date out DATE ) IS BEGIN SELECT COUNT (*) INTO count_total FROM "t_role" WHERE "role_name" LIKE '%' || p_role_name || '%' ; SELECT SYSDATE INTO exec_date FROM dual; END ;
public class PdCountRoleParams { private String roleName; private int total; private Date execDate; }
<select id="countRole" parameterType="com.xc.pojo.procedures.PdCountRoleParams" statementType="CALLABLE"> {call count_role(#{roleName, mode=IN, jdbcType=VARCHAR}, #{total, mode=OUT, jdbcType=INTEGER}, #{execDate, mode=OUT, jdbcType=DATE})} </select>
•指定statemetType为CALLABLE,说明它是在使用存储过程,如果不这样声明那么这段代码将会抛出异常。
•定义了parameterType为PdCountRoleParams参数。
•在调度存储过程中放入参数对应的属性,并且在属性上通过mode设置了其输入或者输出参数,指定对应的jdbcType,这样MyBatis就会使用对应的typeHandler去处理对应的类型转换。
PdCountRoleParams params = new PdCountRoleParams();
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtils.openSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
params.setRoleName("法师");
roleMapper.countRole(params);
System.out.println(params.getRoleName());
System.out.println(params.getTotal());
System.out.println(params.getExecDate());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
游标的使用
在实际应用中,除了使用简易的输入输出参数,有时候也可能使用游标,MyBatis也对存储过程的游标提供了支持。如果把jdbcType声明为CURSOR,那么它就会使用ResultSet对象处理对应的结果,只要设置映射关系,MyBatis就会把结果集映射出来。
这里依旧使用Oracle数据库,先来假设这样的需求:根据角色名称(role_name)模糊查询角色表的数据,但要求支持分页查询,于是存在start和end两个分页参数。为了知道是否存在下一页,还会要求查询出总数(total),于是便存在这样的一个存储过程,如代码清单所示。
CREATE OR REPLACE PROCEDURE find_role ( p_role_name IN VARCHAR, p_start IN INT, p_end IN INT, r_count out INT, ref_cur out sys_refcursor ) AS BEGIN SELECT COUNT (*) INTO r_count FROM "t_role" WHERE "role_name" LIKE '%' || p_role_name || '%'; OPEN ref_cur FOR SELECT "id", "role_name", "note" FROM ( SELECT A.*, ROWNUM AS row1 FROM "t_role" A WHERE A."role_name" LIKE '%' || p_role_name || '%' AND ROWNUM <= p_end ) WHERE row1 > p_start ; END find_role ;
p_role_name是输入参数角色名称,而p_start和p_end是两个分页输入参数,r_count是计算总数的输出参数,ref_cur是一个游标,它将记录当前页的详细数据。为了使用这个过程,先定制一个POJO——PdFindRoleParams,如代码清单所示。
public class PdFindRoleParams { private String roleName; private int start; private int end; private int total; private List<Role> roleList; }
显然参数是和存储过程一一对应的,而游标是由roleList去存储的,只是这里需要为其提供映射关系,游标映射器,如代码清单所示。
<resultMap type="role" id="roleMap"> <id property="id" column="id"/> <result property="roleName" column="role_name"/> <result property="note" column="note"/> </resultMap> <select id="findRoleCall" parameterType="com.xc.pojo.procedures.PdFindRoleParams" statementType="CALLABLE"> {call find_role(#{roleName, mode=IN, jdbcType=VARCHAR}, #{start, mode=IN, jdbcType=INTEGER}, #{end, mode=IN, jdbcType=INTEGER}, #{total, mode=OUT, jdbcType=INTEGER}, #{roleList,mode=OUT,jdbcType=CURSOR, javaType=ResultSet,resultMap=roleMap})} </select>
先定义了resultMap元素,它定义了映射规则。而在存储过程的调用中,对于roleList,定义了jdbcType为CURSOR,这样就会把结果使用ResultSet对象处理。为了使得ResultSet对应能够映射为POJO,设置resultMap为roleMap,这样MyBatis就会采用配置的映射规则将其映射为POJO了,测试代码如代码清单所示。
PdFindRoleParams params = new PdFindRoleParams(); SqlSession sqlSession = null; try { sqlSession = SqlSessionFactoryUtils.openSqlSession(); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); params.setRoleName("法师"); params.setStart(0); params.setEnd(100); roleMapper.findRoleCall(params); System.out.println(params.getRoleList().size()); System.out.println(params.getTotal()); } catch (Exception ex) { ex.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律