前言
项目中用到了Mybatis调用PostgreSQL存储过程(自定义函数)相关操作,由于PostgreSQL自带数组类型,所以有一个自定义函数的入参就是一个int数组,形如:
CREATE OR REPLACE FUNCTION "public"."func_arr_update"(ids _int4)...
如上所示,参数是一个int数组,Mybatis提供了对调用存储过程的支持,那么PostgreSQL独有的数组类型作为存储过程的参数又将如何处理呢?其实很简单,mybatis提供了typeHandlers可以创建一个数组类型的类型处理器,具体做法为:实现 org.apache.ibatis.type.TypeHandler
接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler
, 然后可以选择性地将它映射到一个 JDBC 类型,先稍作了解,后面再做详细说明,接下来依旧结合一个示例来看看。
创建自定义函数
如图,第一步首先是创建一个用于调用的自定义函数,功能也很简单,遍历参数数组的每一个元素和t_student表的stuid做比较,若一致,则修改那条记录的stuname(在其后拼接一段字符串),该自定义函数的DLL语句如下:
CREATE OR REPLACE FUNCTION "public"."func_arr_update"(ids _int4)
RETURNS "pg_catalog"."void" AS $BODY$
DECLARE
scount INTEGER;
rownum integer := 1;
BEGIN
scount:=array_length(ids,1);
while rownum <= scount LOOP
update t_student set stuname = stuname || ' has been modified. ' where stuid = ids[rownum];
rownum := rownum + 1;
END LOOP;
RETURN;
END
$BODY$
LANGUAGE 'plpgsql' VOLATILE COST 100
;
ALTER FUNCTION "public"."func_arr_update"(ids _int4) OWNER TO "postgres";
很简单,获取到参数数组的长度后开始循环,匹配stuid并更新stuname,直接在数据库调用一下看看结果:
如上图,可以看到成功修改了stuid为101,102和103的stuname,自定义函数已经没问题了,接下来就具体看一下如何通过mybatis调用。
调用自定义函数
mybatis中调用自定义函数很简单,Mapper XML文件中的select元素直接提供了属性支持——statementType,在官方文档中可以看到:
如上图,statementType的值默认是PREPARED,也就是说底层默认会使用jdbc的PreparedStatement,而我们都知道jdbc调用存储过程时需要用CallableStatement,所以在这里我们需要将statementType的值设置为CALLABLE。
mybatis默认的ArrayTypeHandler
调用存储过程很简单,那么接下来的问题是如何在mybatis中传一个数组参数到存储过程中呢?这里就要用到另外一个概念——TypeHandler,这是mybatis提供的自定义类型转换器,mybatis在预编译语句对象(PreparedStatement)设置参数时或是从结果集中取值时都会用类型处理器将获取的值以合适的方式转换成Java类型,mybatis默认实现了一部分TypeHandler供我们使用,当我们没有指定TypeHandler时(大多数情况都不会指定),mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理,下面可以通过查看源码大概看一下默认的TypeHandler,导入源码后可以在org.apache.ibatis.type包下找到一个TypeHandlerRegistry类,typeHandler正是通过这个类管理的,先看一下它的构造方法:
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandle