sql >> Database teknologi >  >> RDS >> Oracle

Udfører masseopdateringer med MyBatis og Oracles lagrede procedurer

En procedure kan tage tabeltypeparametre, og du kan skrive en brugerdefineret typebehandler, der udfører konverteringen.

Det kan være lettere at forklare ved hjælp af konkrete objekter.
I stedet for MY_TYPE , jeg bruger S_USER_OBJ ...

create or replace type S_USER_OBJ as object (
  id integer,
  name varchar(20)
);

...et bord...

create table users (
  id integer,
  name varchar(20)
);

...og en POJO.

public class User {
  private Integer id;
  private String name;
  // setter/getter
}

Her er den nye type, som er en samling af S_USER_OBJ .

create or replace type S_USER_OBJ_LIST as table of S_USER_OBJ;

Proceduren kan tage tabeltypen som parametre. f.eks.

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 ville se ud som følger:

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 er en brugerdefineret typebehandler, der konverterer Liste til/fra en ARRAY af 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;
  }
  ...
}

Koden, der bruger metoden, ville se nogenlunde sådan ud.

Map<String, ?> outParam = new HashMap<>();
mapper.doUpdate(userList, outParam);
List<User> outUsers = outParam.get("outUsers");

For OUT parameter, er der også en anden måde at bruge refcursor og resultatkort.
I mapper-sætningen skal du angive OUT-parameteren som følger.

#{outParam.outUsers,jdbcType=CURSOR,javaType=java.sql.ResultSet,mode=OUT,resultMap=userRM}

Resultatkortet er ret ligetil.

<resultMap type="test.User" id="userRM">
  <id property="id" column="id" />
  <result property="name" column="name" />
</resultMap>

I proceduren skal du erklære OUT-parameteren som 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;

Her er en eksekverbar demo:
https://github .com/harawata/mybatis-issues/tree/master/so-56834806




  1. Opbygning af mysql-forespørgsel til at tælle specifikke data under samme tabel/række

  2. ændre begivenhedsplanlægningsstatus i mysql

  3. Python + MySQLdb eksekvering

  4. Ulemper ved at citere heltal i en Mysql-forespørgsel?