1. 程式人生 > >使用mybatis給Oracle儲存過程傳List,陣列型別.

使用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無法自動處理,

繼承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();
            }
        }

    }
}
 
如果沒有在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大概就這些了.有什麼不對的,歡迎不吝賜教