java - 使用 MyBatis 和 Oracle 存储过程进行批量更新
问题描述
使用 MyBatis 和 Oracle 的存储过程进行批量数据库更新的遗留解决方案。当前版本的 Mapper 看起来与此类似
@Mapper
public interface MyMapper {
void doUpdate(@Param("in") Map<String, List> in,
@Param("out") Map<String, List> out);
}
这个想法是提供一个长度相同的列表映射,其中字段值作为“in”参数使用这些列表作为参数来调用这样的存储过程
<select id="doUpdate"
statementType="CALLABLE">
<![CDATA[
{
CALL doUpdate(
#{in.field1, mode=IN, jdbcType=ARRAY, jdbcTypeName=MY_TYPE, typeHandler=NumberTypeHandler },
#{in.field2, mode=IN, jdbcType=ARRAY, jdbcTypeName=MY_TYPE, typeHandler=NumberTypeHandler},
#{in.field3, mode=IN, jdbcType=ARRAY, jdbcTypeName=MY_TYPE, typeHandler=NumberTypeHandler},
#{out.field1, mode=IN, jdbcType=ARRAY, jdbcTypeName=MY_TYPE, typeHandler=NumberTypeHandler })}]]>
</select>
然后在存储过程中遍历这些数组以一一更新实体。
问题是我必须在调用之前初始化所有 Maps/Arrays 并手动填充它们,并且还要手动将结果转换回 Java 对象。所以现在它看起来太复杂和冗长了,我正试图找到一个更准确的解决方案。
所以问题是:有没有一种更简单的方法可以使用 MyBatis 向存储过程提供对象列表?我尝试了 parameterMap 但在我的情况下实际参数类型应该是 List 并且该 List 的元素应该是自定义 Java 对象,所以我没有设法使用这种方法找到合适的解决方案。
解决方案
过程可以采用表类型参数,您可以编写自定义类型处理程序来执行转换。
使用具体对象可能更容易解释。
而不是MY_TYPE
,我将使用S_USER_OBJ
...
create or replace type S_USER_OBJ as object (
id integer,
name varchar(20)
);
...一张桌子...
create table users (
id integer,
name varchar(20)
);
...和一个 POJO。
public class User {
private Integer id;
private String name;
// setter/getter
}
这是新类型,它是S_USER_OBJ
.
create or replace type S_USER_OBJ_LIST as table of S_USER_OBJ;
该过程可以将表类型作为参数。例如
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;
映射器将如下所示:
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
是一个自定义类型处理程序,可以转换为/List<User>
从ARRAY
.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;
}
...
}
使用该方法的代码看起来像这样。
Map<String, ?> outParam = new HashMap<>();
mapper.doUpdate(userList, outParam);
List<User> outUsers = outParam.get("outUsers");
对于OUT
参数,还有另一种使用 refcursor 和 result map 的方法。
在 mapper 语句中,指定 OUT 参数,如下所示。
#{outParam.outUsers,jdbcType=CURSOR,javaType=java.sql.ResultSet,mode=OUT,resultMap=userRM}
结果映射非常简单。
<resultMap type="test.User" id="userRM">
<id property="id" column="id" />
<result property="name" column="name" />
</resultMap>
在过程中,将 OUT 参数声明为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;
这是一个可执行的演示:
https ://github.com/harawata/mybatis-issues/tree/master/so-56834806
推荐阅读
- java - Firebase 数据库查询未显示在屏幕上
- javascript - 如何使用 Event Emitter 接收多个相同类型的事件?
- java - J2SE和J2EE中的2代表什么?
- python - 'array[:] = something' 和 'array = something' 之间的区别
- python - 具有已定义名称范围的 Pandas 数据框到 Excel
- accessibility - 辅助功能:相关内容 - 两指轻弹
- sql - 分配一个唯一的 id sql server
- sql - 如何根据行号动态创建新列
- vue.js - 无法从 rootState 访问 Vuex 模块
- r - 如何使用windows scheduler执行R脚本