使用mybatis给Oracle存储过程传List,数组类型.

0.不废话.直接上过程

1.因为Oracle本身是有数组概念的.我们直接使用Oracle中的数组,但需要先创建类型,varray也就是Oracle的数组,100指定长度,of后面指定类型,

CREATE OR REPLACE TYPE tables_array AS VARRAY(100) OF VARCHAR2(32);

2.第二步,创建一个表,进行测试数据,

drop table test;
create table test
(
  name varchar2(32)
);

3.第三步,创建一个测试过程.,传入自定义的数组类型,使用数组内部方法.遍历添加

create or replace procedure t_list_to_p(arr_t in tables_array) is
begin
  for i in arr_t.first .. arr_t.last loop
    insert  into test values(arr_t(i));
  end loop;
  commit;
end t_list_to_p;

在这里扩展下Oracle数组的操作方法以及属性

COUNT             返回集合中元素的个数   
DELETE            删除集合中所有元素    
DELETE(x)         删除元素下标为x的元素      对VARRAY非法     
DELETE(x,y)       删除元素下标从X到Y的元素   对VARRAY非法     

EXIST(x)          如果集合元素x已经初始化,则返回TRUE, 否则返回FALSE    
EXTEND            在集合末尾添加一个元素      对Index_by非法    
EXTEND(x)         在集合末尾添加x个元素       对Index_by非法    
EXTEND(x,n)       在集合末尾添加元素n的x个副本 对Index_by非法    
FIRST             返回集合中的第一个元素的下标号,对于VARRAY集合始终返回1。    

LAST              返回集合中最后一个元素的下标号, 对于VARRAY返回值始终等于COUNT.    
LIMIT             返回VARRY集合的最大的元素个数   Index_by集合和嵌套表无用    
NEXT(x)           返回在第x个元素之后及紧挨着它的元素值,如果x是最后一个元素,返回null.    
PRIOR(x)          返回在第x个元素之前紧挨着它的元素的值,如果x是第一个元素,则返回null。    
TRIM              从集合末端开始删除一个元素  对于index_by不合法    

TRIM(x)           从集合末端开始删除x个元素

4.plsql中调用赋值,这个我找了好久.在这展示下.用于测试过程(指存储过程)是否好用.只要是怎么给数组赋值,如果是对象的话,table_list(able_obj('12')),关于对象类型,我会在写一篇

declare
  -- Non-scalar parameters require additional processing 
  arr_t tables_array:=tables_array('13','14','15');
begin
  -- Call the procedure
  t_list_to_p(arr_t => arr_t);
end;

5.到这里函数这里就准备好了.至于如何用jdbc测试.就不说了.网上很多,接下来讲如何用mybatis进行调用.先把mybaitis环境搭建好.这里就不贴代码了.

6.编写map.xml文件调用,以及对应的接口

void appCurrentRoad(@Param("roads")List<String> roads);

 下面的.参数最好都写上.而typeHandler则是必须的.也是我自定义的一个,

<select id="appCurrentRoad" statementType="CALLABLE" >
        call t_list_to_p(
         #{roads,jdbcType=ARRAY,javaType=List,typeHandler=com.hollycrm.emscheck.common.util.ArrayTypeHandler,mode=IN}
        )
</select>

7.编写自定义的typeHandler.因为我们在Oracle声明的自定义数组.是自定义类型的.所以mybatis无法自动处理,

部分详细内容.可以看下https://www.jianshu.com/p/23d6a0d07a87

https://blog.csdn.net/wlwlwlwl015/article/details/52526630有讲解typeHandler的部分源码,

继承BaseTypeHandler重写对应方法

package com.common.util;


import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;


//这个注解声明了他是处理jdbc类型的  @MappedTypes()也可以用这个注解指定javaType.进行约束,一般不用
@MappedJdbcTypes(JdbcType.ARRAY)
public class ArrayTypeHandler extends BaseTypeHandler{

    @Override
    public Object getNullableResult(ResultSet arg0, String arg1)
            throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return null;
    }

    @Override
    public Object getNullableResult(CallableStatement arg0, int arg1)
            throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }
    //这个方法是将java中的集合类型转换成数据库中对应类型.在这里也就是数组类型
    @SuppressWarnings("unchecked")
    @Override
    public void setNonNullParameter(java.sql.PreparedStatement parameterSetter, int i,
                                    Object o, JdbcType jdbcType) throws SQLException {
        Connection conn = null;
        try {
            if(null != o){
                List<String> list = (ArrayList<String>) o;
                conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost", "test", "test");
                //这里必须得用大写,而且必须要引入一个包,如果不引入这个包的话字符串无法正常转换,包是:orai18n.jar(这个并没试过)
                //这个应该是封装的一个转换方法吧.不是很清楚
                ArrayDescriptor arrayDes = ArrayDescriptor.createDescriptor("TABLES_ARRAY",conn);
                //这里是声明一个数据库的数组类型
                ARRAY array = new ARRAY(arrayDes,conn,list.toArray());
                parameterSetter.setArray(i, array);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            if(null != conn){
                conn.close();
            }
        }

    }
}

8.差不多就是这些了. 参照帖子https://blog.csdn.net/hzw2312/article/details/8444462

 
如果没有在Handler中注释jdbcType也可在这里设置 
Xml代码  

<typeHandlers>  
        <typeHandler javaType="list" <!--jdbcType="ARRAY"--> handler="com.package.MyHandler"/>  
</typeHandlers> 

9.最后在总结下遇到的错误

这个错误是我在没有指定@MappedJdbcTypes(JdbcType.ARRAY)这个注解的时候抛出的.在有就是各参数是否对应,如最后的mode是否是in 或者out 或者inout类型

ORA-03115: unsupported network datatype or representation 不支持的网络数据类型或表示

最开始的错误.在没有自定义TypeHandler时

TypeException: Could not set parameters for mapping: ParameterMapping{property='roads', mode=IN, javaType=class java.lang.Object, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null',expression='null'..SQLException: 无效的列类型

10.ok大概就这些了.有什么不对的,欢迎不吝赐教

猜你喜欢

转载自blog.csdn.net/qq_40846086/article/details/83184303