Eine Prozedur kann Tabellentypparameter annehmen und Sie können einen benutzerdefinierten Typhandler schreiben, der die Konvertierung durchführt.
Es ist möglicherweise einfacher, dies anhand konkreter Objekte zu erklären.
Anstelle von MY_TYPE
, verwende ich S_USER_OBJ
...
create or replace type S_USER_OBJ as object (
id integer,
name varchar(20)
);
...ein Tisch...
create table users (
id integer,
name varchar(20)
);
...und ein POJO.
public class User {
private Integer id;
private String name;
// setter/getter
}
Hier ist der neue Typ, der eine Sammlung von S_USER_OBJ
ist .
create or replace type S_USER_OBJ_LIST as table of S_USER_OBJ;
Die Prozedur kann den Tabellentyp als Parameter übernehmen. z. B.
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;
Mapper würde wie folgt aussehen:
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
ist ein benutzerdefinierter Typhandler, der List<User>
konvertiert zu/von einem ARRAY
von 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;
}
...
}
Der Code, der die Methode verwendet, würde in etwa so aussehen.
Map<String, ?> outParam = new HashMap<>();
mapper.doUpdate(userList, outParam);
List<User> outUsers = outParam.get("outUsers");
Für OUT
Parameter, es gibt auch einen anderen Weg mit Refcursor und Ergebnis-Map.
In der Mapper-Anweisung spezifizieren Sie den OUT-Parameter wie folgt.
#{outParam.outUsers,jdbcType=CURSOR,javaType=java.sql.ResultSet,mode=OUT,resultMap=userRM}
Die Ergebniskarte ist ziemlich einfach.
<resultMap type="test.User" id="userRM">
<id property="id" column="id" />
<result property="name" column="name" />
</resultMap>
Deklarieren Sie in der Prozedur den OUT-Parameter als 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;
Hier ist eine ausführbare Demo:
https://github .com/harawata/mybatis-issues/tree/master/so-56834806