Reputation: 347
I have a Oracle SP with the parameters as below
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;
/
while the "file name" is a string array in Oracle. The bean is defined as below:
@Data
public class BaseEntity {
private String name;
private String[] filename;
}
I want to pass the entire bean to Oracle stored procedure.
In my mapper.java
@Mapper
public interface BaseMapper {
void add(BaseEntity d);
}
In my BaseMapper.xml
<select id="add" statementType="CALLABLE" parameterType="BaseEntity">
call create_deliverable(
#{name},
#{filename,jdbcType=ARRAY, typeHandler=ArrayTypeHandler}
)
</select>
I attempted to write up a type handler to deal with the case. But I failed in the part to work it out.
Here's what's failed:
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();
}
}
The failure is related to the part of "createArrayOf". It reads the arrayTypeName as VARCHAR, which is correct
Here's the error message:
Could not set parameters for mapping: ParameterMapping{property='filename', mode=IN, javaType=class java.util.ArrayList, jdbcType=ARRAY, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}. Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter #2 with JdbcType ARRAY
Any input will be greatly appreciated.
Thanks
Upvotes: 0
Views: 2765
Reputation: 3594
Oracle's JDBC driver (as of version 19.8.0.0) does not support java.sql.Connection#createArrayOf()
which is used by MyBatis' built-in ArrayTypeHandler
, unfortunately.
So, you need to write a custom type handler.
I just tested and the following implementation worked.
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();
}
...
And specify the type handler in the parameter reference.
call create_deliverable(
#{name},
#{filename,jdbcType=ARRAY,typeHandler=test.OracleStringArrayTypeHandler}
)
Upvotes: 0
Reputation: 347
@ave has given the correct answer. Just because I'm using springboot I'm here to recap the solution:
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'
Here's my 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
Here's my datasource 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)Here's my entity bean(I created new ones to not change the previous ones):
@Data
public class TestEntity {
private String name;
private String[] filename;
}
3)Here's my mapper java:
@Mapper
public interface TestMapper {
void add(TestEntity t);
}
<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>
please note the "filename" parameter needs to be assigned to the typeHandler as mybatis default one won't work
5)the OracleStringArrayTypeHandler => Refers to ave's answer. There are a few more methods to be implemented but the key is the set parameter & I used
import oracle.jdbc.OracleConnection;
instead of
oracle.jdbc.driver.OracleDriver;
as the 2nd one has been deprecated
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;
/
Thanks for @ave again. Without the help from @ave I couldn't move forward for days. I was thinking of use delimiter seperated string instead previously but absolutely string [] is much more advanced & convinient
Upvotes: 1