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所以这里总结一下解决方法:
- 我使用的是默认的 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);
}
- 这是我的映射器 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;
因为第二个已被弃用
- 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 的帮助,我好几天都无法前进。我之前考虑过使用定界符分隔的字符串,但绝对字符串 [] 更高级和方便
我有一个 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所以这里总结一下解决方法:
- 我使用的是默认的 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);
}
- 这是我的映射器 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;
因为第二个已被弃用
- 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 的帮助,我好几天都无法前进。我之前考虑过使用定界符分隔的字符串,但绝对字符串 [] 更高级和方便