使用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.youkuaiyun.com/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.youkuaiyun.com/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大概就这些了.有什么不对的,欢迎不吝赐教

<think>嗯,用户问的是Java如何给SQL数组。这个问题看起来挺常见的,但具体怎么实现呢?首先我得回忆一下Java和数据库交互的方式。通常我们会用JDBC或者一些框架,比如MyBatis、Hibernate之类的。不同的方法可能有不同的处理方式。 首先,用户可能需要传递数组作为SQL查询的参数,比如在IN子句中使用。例如,SELECT * FROM table WHERE id IN (?, ?, ?)。这里的问题是如何动态地处理不同长度的数组,特别是当数组长度不固定的时候。 用JDBC的话,直接数组好像不太行,因为JDBC的PreparedStatement不支持直接设置数组参数,除非是数据库特定的数组类型,比如PostgreSQL的ARRAY。这时候可能需要根据不同的数据库来处理。例如,使用setArray方法,但这样的话,数据库驱动必须支持这个功能。比如PostgreSQL的JDBC驱动允许这样用,但其他数据库比如MySQL可能不支持,可能需要转换成字符串拼接或者其他方式。 不过用户可能更想要通用的解决方案。比如,手动生成多个占位符,然后循环设置参数。例如,数组有n个元素,就生成n个问号,然后逐个设置参数。这种方法虽然繁琐,但是兼容性比较好。不过如果数组长度变化的话,每次都要重新生成SQL语句,可能会有SQL注入的风险,不过用PreparedStatement的话,应该还是安全的,因为参数是预编译的。 另外,用户可能听说过使用MyBatis这样的ORM框架,MyBatis提供了<foreach>标签,可以在XML映射文件中动态生成IN子句里的占位符,这样就能方便地处理数组参数。这时候需要确保传入参数List或者数组类型,然后在XML里遍历。这可能是一个更简洁的方法,但用户需要在使用MyBatis的情况下才能这么做。 还有可能用户遇到的问题是如何传递数组类型参数,比如存储过程需要接收数组。这时候可能需要使用数据库特定的类型,比如Oracle的ARRAY类型,这时候在Java中需要创建对应的SQL数组对象,通过connection.createArrayOf()方法,然后用PreparedStatement的setArray方法传入。不过这也依赖于数据库的支持。 可能还需要考虑性能问题,如果数组很大,直接拼接成IN子句可能会导致性能问题,这时候可能需要其他方法,比如临时表或者使用JOIN。但用户的问题可能更关注基础的方法。 总结下来,可能的解决方案有几种: 1. 使用PreparedStatement手动拼接占位符,适用于所有数据库,但代码量较多。 2. 使用数据库特定的数组类型,比如PostgreSQL的setArray,但仅限于特定数据库。 3. 使用ORM框架如MyBatis的动态SQL功能,简化代码。 4.数组转换为字符串传递在SQL中处理,比如使用FIND_IN_SET,但需要注意分隔符和性能。 需要分步骤说明这些方法,指出各自的优缺点,以及适用场景。同时提醒用户注意SQL注入的问题,尽管PreparedStatement可以防止注入,但手动拼接SQL字符串可能会有风险,所以必须使用参数化查询。 另外,可能用户对不同类型数组参数传递不太清楚,比如基本类型数组和对象数组,是否需要做特殊处理。比如在JDBC中,使用setArray时可能需要将Java数组转换为SQL数组对象。 可能还需要提供代码示例,比如用JDBC手动设置参数的例子,或者MyBatis的XML配置例子,这样用户更容易理解。 最后,要确保回答结构清晰,步骤明确,帮助用户一步步解决问题,且覆盖不同的情况和可能的陷阱,比如数据库兼容性、性能、安全性等。</think>在Java中向SQL传递数组时,通常需要根据数据库类型和具体使用场景选择合适的方法。以下是分步骤实现的常见方案: --- ### 一、使用PreparedStatement手动拼接(通用方法) 适用于所有数据库,但需手动处理占位符。 ```java List<Integer> ids = Arrays.asList(1, 2, 3); String sql = "SELECT * FROM table WHERE id IN (" + String.join(",", Collections.nCopies(ids.size(), "?")) + ")"; try (PreparedStatement ps = connection.prepareStatement(sql)) { for (int i = 0; i < ids.size(); i++) { ps.setInt(i + 1, ids.get(i)); } ResultSet rs = ps.executeQuery(); // 处理结果... } ``` --- ### 二、数据库特定数组支持(如PostgreSQL) 使用`setArray()`方法直接传递数组: ```java Integer[] ids = {1, 2, 3}; try (PreparedStatement ps = connection.prepareStatement( "SELECT * FROM table WHERE id = ANY(?)")) { Array array = connection.createArrayOf("integer", ids); ps.setArray(1, array); ResultSet rs = ps.executeQuery(); // 处理结果... } ``` --- ### 三、使用MyBatis动态SQL(推荐ORM场景) 在MyBatis的XML映射文件中使用`<foreach>`标签: ```xml <select id="selectByIds" resultType="YourEntity"> SELECT * FROM table WHERE id IN <foreach item="id" collection="ids" open="(" separator="," close=")"> #{id} </foreach> </select> ``` Java调用时直接传递`List`或数组: ```java List<Integer> ids = Arrays.asList(1, 2, 3); List<YourEntity> result = sqlSession.selectList("selectByIds", ids); ``` --- ### 四、字符串拼接方案(谨慎使用) 仅适用于简单场景,需注意SQL注入和分隔符冲突: ```sql SELECT * FROM table WHERE FIND_IN_SET(id, ?) ``` Java代码: ```java String ids = "1,2,3"; ps.setString(1, ids); ``` --- ### 关键注意事项 1. **防SQL注入**:始终优先使用`PreparedStatement`或ORM工具的参数化查询。 2. **性能优化**:当数组过大时(如超过1000个元素),建议改用临时表或分批次查询。 3. **数据库兼容性**:`ANY()`或`ARRAY`语法需检查数据库支持情况(如PostgreSQL/Oracle支持,MySQL不支持)。 4. **类型匹配**:确保Java数组类型与数据库字段类型一致(如`Integer[]`对应`INTEGER[]`)。 根据具体需求选择最适合的方案,可兼顾代码简洁性和数据库兼容性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值