Mybatis - 将 String[] 传递给 Oracle 存储过程

Mybatis - Passing String[] to Oracle stored procedure

我有一个 Oracle SP,其参数如下

create type stringArray as table of varchar2(30)
/

CREATE OR REPLACE PROCEDURE create_deliverable
(
    in_p_name varchar2,
    in_p_filename stringArray 
)AS
  ret_ID number;
BEGIN
...
END;
/

而“文件名”在Oracle中是一个字符串数组。 bean 定义如下:

@Data
public class BaseEntity {
    private String name;
    private String[] filename;
}

我想将整个 bean 传递给 Oracle 存储过程。

在我的mapper.java

@Mapper
public interface BaseMapper {


    void add(BaseEntity d);
}

在我的BaseMapper.xml

<select id="add"  statementType="CALLABLE" parameterType="BaseEntity">
         call create_deliverable(
             #{name},
             #{filename,jdbcType=ARRAY, typeHandler=ArrayTypeHandler}
         )
    </select>

我试图编写一个类型处理程序来处理这种情况。但是我在这部分没能解决。

失败的原因如下:

public class ArrayTypeHandler extends BaseTypeHandler<Object> {
  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
          throws SQLException {

      Class<?> componentType = parameter.getClass().getComponentType();
      String arrayTypeName = resolveTypeName(componentType);

      Array array = ps.getConnection().createArrayOf(arrayTypeName, (Object[]) parameter);
      ps.setArray(i, array);
      array.free();
  }
}

失败与“createArrayOf”部分有关。它将 arrayTypeName 读取为 VARCHAR,这是正确的

错误信息如下:

无法设置映射参数:ParameterMapping{属性='filename', mode=IN, javaType=class java.util.ArrayList, jdbcType=数组,numericScale=null,resultMapId='null',jdbcTypeName='null',表达式='null'}。原因:org.apache.ibatis.type.TypeException:使用 JdbcType ARRAY

为参数 #2 设置非空值时出错

任何输入将不胜感激。

谢谢

Oracle 的 JDBC 驱动程序(自版本 19.8.0.0 起)不支持 MyBatis 内置 ArrayTypeHandler 使用的 java.sql.Connection#createArrayOf(),很遗憾。

因此,您需要编写自定义类型处理程序。
我刚刚测试并且以下实现有效。

package test;

import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import oracle.jdbc.OracleConnection;

public class OracleStringArrayTypeHandler extends BaseTypeHandler<String[]> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i,
      String[] parameter, JdbcType jdbcType) throws SQLException {
    OracleConnection conn = ps.getConnection().unwrap(OracleConnection.class);
    Array array = conn.createOracleArray("STRINGARRAY", parameter);
    ps.setArray(i, array);
    array.free();
  }
  ...

并在参数引用中指定类型处理程序。

call create_deliverable(
  #{name},
  #{filename,jdbcType=ARRAY,typeHandler=test.OracleStringArrayTypeHandler}
)

@ave 给出了正确答案。 正因为我用的是springboot所以这里总结一下解决方法:

  1. 我使用的是默认的 hikari 池,而且我之前没有接触过数据源。它是自动装配的。需要覆盖,否则会弹出下面的错误信息

Cause: java.lang.ClassCastException: class com.zaxxer.hikari.pool.HikariProxyConnection cannot be cast to class oracle.jdbc.OracleConnection (com.zaxxer.hikari.pool.HikariProxyConnection and oracle.jdbc.OracleConnection are in unnamed module of loader 'app'

这是我的 application.yml

spring: application: name: tools datasource: username: myuser password: mypassword url: jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost) (PORT=1521))(CONNECT_DATA= (SERVICE_NAME=orclpdb))) driver-class-name: oracle.jdbc.OracleDriver

这是我的数据源 bean

    import oracle.jdbc.pool.OracleDataSource;

    @Value("${spring.datasource.username}")
    String username;

    @Value("${spring.datasource.password}")
    String password;

    @Value("${spring.datasource.url}")
    String url;
    @Bean
    DataSource oracleDataSource() throws SQLException {

        OracleDataSource dataSource = new OracleDataSource();
        dataSource.setUser(username);
        dataSource.setPassword(password);
        dataSource.setURL(url);
        return dataSource;
    }

2)这是我的实体bean(我创建了新的不改变之前的):

@Data
public class TestEntity {
    private String name;
    private String[] filename;
}

3) 这是我的映射器 java:

@Mapper
public interface TestMapper {
    void add(TestEntity t);
}
  1. 这是我的映射器 xml:
    <select id="add"  statementType="CALLABLE" parameterType="org.ssc.gss.entity.TestEntity">
        call create_deliverable_test(
            #{name,mode=IN},
            #{filename,mode=IN,jdbcType=ARRAY,javaType=ArrayList, typeHandler=OracleStringArrayTypeHandler}
            )
    </select>

请注意,需要将“文件名”参数分配给 typeHandler,因为 mybatis 默认值将不起作用

5)OracleStringArrayTypeHandler => 参考ave的回答。还有一些方法要实现,但关键是设置参数,我使用了

import oracle.jdbc.OracleConnection;

而不是

oracle.jdbc.driver.OracleDriver;

因为第二个已被弃用

  1. Oracle 存储过程部分:
create type stringArray as table of varchar2(30)
/

CREATE OR REPLACE PROCEDURE create_deliverable_test
(
    in_p_name varchar2,
    in_p_filename stringArray 
)AS
  ret_ID number;
BEGIN
...
END;
/

再次感谢@ave。没有@ave 的帮助,我好几天都无法前进。我之前考虑过使用定界符分隔的字符串,但绝对字符串 [] 更高级和方便