Une procédure peut prendre des paramètres de type table et vous pouvez écrire un gestionnaire de type personnalisé qui effectue la conversion.
Il peut être plus facile d'expliquer en utilisant des objets concrets.
Au lieu de MY_TYPE
, je vais utiliser S_USER_OBJ
...
create or replace type S_USER_OBJ as object (
id integer,
name varchar(20)
);
...un tableau...
create table users (
id integer,
name varchar(20)
);
...et un POJO.
public class User {
private Integer id;
private String name;
// setter/getter
}
Voici le nouveau type qui est une collection de S_USER_OBJ
.
create or replace type S_USER_OBJ_LIST as table of S_USER_OBJ;
La procédure peut prendre le type de table en paramètre. ex.
create or replace procedure doUpdate(
user_list in S_USER_OBJ_LIST,
user_out out S_USER_OBJ_LIST
) is
begin
-- process IN param
for i in user_list.first .. user_list.last loop
update users
set name = user_list(i).name)
where id = user_list(i).id;
end loop;
-- set OUT param
select * bulk collect into user_out
from (
select S_USER_OBJ(u.id, u.name) from users u
);
end;
Le mappeur ressemblerait à ceci :
void doUpdate(
@Param("users") List<User> users,
@Param("outParam") Map<String, ?> outParam);
<update id="doUpdate" statementType="CALLABLE">
{call doUpdate(
#{users,typeHandler=pkg.UserListTypeHandler},
#{outParam.outUsers,jdbcType=ARRAY,jdbcTypeName=S_USER_OBJ_LIST,mode=OUT,typeHandler=pkg.UserListTypeHandler}
)}
</update>
UserListTypeHandler
est un gestionnaire de type personnalisé qui convertit List<User>
vers/depuis un ARRAY
de STRUCT
.
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import oracle.jdbc.driver.OracleConnection;
public class UserListTypeHandler extends
BaseTypeHandler<List<User>>{
@Override
public void setNonNullParameter(
PreparedStatement ps, int i, List<User> parameter,
JdbcType jdbcType) throws SQLException {
Connection conn = ps.getConnection();
List<Struct> structs = new ArrayList<Struct>();
for (int idx = 0; idx < parameter.size(); idx++) {
User user = parameter.get(idx);
Object[] result = { user.getId(), user.getName() };
structs.add(conn.createStruct("S_USER_OBJ", result));
}
Array array = ((OracleConnection) conn)
.createOracleArray("S_USER_OBJ_LIST",
structs.toArray());
ps.setArray(i, array);
array.free();
}
@Override
public List<User> getNullableResult(
CallableStatement cs,
int columnIndex) throws SQLException {
List<User> result = new ArrayList<>();
Array array = cs.getArray(columnIndex);
Object[] objs = (Object[]) array.getArray();
for (Object obj : objs) {
Object[] attrs = ((Struct) obj).getAttributes();
result.add(new User(
((BigDecimal) attrs[0]).intValue(),
(String) attrs[1]));
}
array.free();
return result;
}
...
}
Le code utilisant la méthode ressemblerait à ceci.
Map<String, ?> outParam = new HashMap<>();
mapper.doUpdate(userList, outParam);
List<User> outUsers = outParam.get("outUsers");
Pour OUT
, il existe également un autre moyen d'utiliser le refcursor et la carte de résultat.
Dans l'instruction du mappeur, spécifiez le paramètre OUT comme suit.
#{outParam.outUsers,jdbcType=CURSOR,javaType=java.sql.ResultSet,mode=OUT,resultMap=userRM}
La carte de résultat est assez simple.
<resultMap type="test.User" id="userRM">
<id property="id" column="id" />
<result property="name" column="name" />
</resultMap>
Dans la procédure, déclarez le paramètre OUT comme SYS_REFCURSOR
create or replace procedure doUpdate(
user_list in S_USER_OBJ_LIST,
user_out out SYS_REFCURSOR
) is
begin
...
-- set OUT param
open user_out for select * from users;
end;
Voici une démo exécutable :
https://github .com/harawata/mybatis-issues/tree/master/so-56834806