Oracle
 sql >> Base de données >  >> RDS >> Oracle

Faire des mises à jour en masse avec les procédures stockées MyBatis et Oracle

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