Mybatis调用数据库存储过程,参数怎么根据数据库存储过程里面的参数动态提交

Mybatis调用数据库存储过程时,怎么根据数据库存储过程最新的参数,来动态调整call里面的参数。

现在是数据库存储过程里面入参和出参会随时修改,也会调整入参和出参顺序。现在数据库那里要求,后台Mybatis在调用存储过程,需要根据最新的参数和顺序动态调整。但前提是不能修改代码,不能重新发布。必须在调用数据库存储过程前,通过一个通用方法来获取存储过程最新的参数和顺序。然后将最新的参数和顺序传到mapper.xml文件中

这是之前的写法,通过字段名和数据库存储过程一一对应

call bsp_CommRole;8(
                 #{action,mode=IN,jdbcType=VARCHAR},
                #{lan_id,mode=IN,jdbcType=INTEGER},
                #{branch_id,mode=IN,jdbcType=VARCHAR},
                #{column_title,mode=OUT,jdbcType=VARCHAR},
               #{column_length,mode=OUT,jdbcType=VARCHAR},
                #{column_align,mode=OUT,jdbcType=VARCHAR}
 )

现在通过 [sys].[sp_procedure_params_100_managed] @procedure_name=N'存储过程名' 可以获取最新的参数字段及输入输出类型,但是需要将最新的参数动态传到call bsp_CommRole;8里面

现在在调用存储过程,里面参数名和mode不能直接写死,需要在接口调用的地方,先通过上面的sp_procedure_params_100_managed获取最新的参数,然后不管通过什么方法把最新的参数传到call里面。这样以后数据库修改存储过程后,后端不用跟着修改调用参数也能正常调用。

这个问题困扰了好几天,如能解决,感激不尽

小魔女参考了bing和GPT部分内容调写:
Mybatis调用数据库存储过程时,参数动态提交的方法有很多,下面介绍一种实现方式。

首先,在Mybatis中,可以使用标签,来实现动态提交参数。可以通过在mapper.xml文件中定义一个list集合,然后再使用标签来遍历list集合,将list集合里面的参数动态传入到call里面。

具体的实现步骤如下:

  1. 首先通过存储过程获取最新的参数和参数的顺序;
  2. 然后在mapper.xml文件中定义一个list集合,将最新的参数和顺序放入list集合中;
  3. 最后在调用存储过程时使用标签来遍历list集合,将list集合里面的参数动态传入到call里面。

例如:

<select id="callProcedure" parameterType="map" statementType="CALLABLE">
    {call bsp_CommRole(
        <foreach collection="paramList" item="item" index="index" open="" separator="," close="">
            #{item.paramName, mode = #{item.paramMode}, jdbcType = #{item.jdbcType}}
        </foreach>)}
</select>

以上就是Mybatis调用数据库存储过程时,根据数据库存储过程最新的参数动态提交的一种实现方式。
回答不易,记得采纳呀。

该回答引用ChatGPT

如有疑问可以回复我

根据您的需求,需要在调用存储过程之前获取最新的参数字段及输入输出类型,并将其动态传递到MyBatis的mapper.xml文件中,以便调用存储过程。

这可以通过以下步骤实现:

1、创建一个Java类,该类使用JDBC连接到数据库并调用存储过程 [sys].[sp_procedure_params_100_managed],获取最新的参数字段及其输入输出类型。

2、在该Java类中,将获取到的参数信息动态构建为一个包含参数名、输入/输出类型和参数值的Map对象。可以使用HashMap或者其他类型的Map来存储参数信息。

3、将上述Map对象传递给MyBatis的mapper.xml文件。可以使用MyBatis提供的动态SQL语句构建工具,在mapper.xml文件中动态构建调用存储过程的SQL语句。例如,可以使用标签遍历Map对象中的参数,并动态构建存储过程的调用语句。

4、在Java代码中调用MyBatis mapper接口,将上述动态构建的SQL语句作为参数传递给MyBatis mapper方法,即可执行存储过程并获取结果。

下面是一个示例Java类,该类演示了如何使用JDBC连接到数据库并调用存储过程 [sys].[sp_procedure_params_100_managed],获取最新的参数字段及其输入输出类型,并将其动态构建为一个Map对象。

import java.sql.*;
import java.util.*;

public class ProcedureParamMapper {
    private static final String URL = "jdbc:sqlserver://localhost:1433;databaseName=MyDatabase";
    private static final String USERNAME = "myuser";
    private static final String PASSWORD = "mypassword";
    
    public Map<String, Object> getProcedureParams(String procedureName) throws SQLException {
        Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        Map<String, Object> paramInfo = new HashMap<>();
        try {
            CallableStatement statement = connection.prepareCall("{call [sys].[sp_procedure_params_100_managed](?)}");
            statement.setString(1, procedureName);
            ResultSet resultSet = statement.executeQuery();
            while (resultSet.next()) {
                String paramName = resultSet.getString("parameter_name");
                int paramMode = resultSet.getInt("parameter_mode");
                int dataType = resultSet.getInt("data_type");
                String typeName = resultSet.getString("type_name");
                paramInfo.put(paramName, getJdbcType(paramMode, dataType, typeName));
            }
        } finally {
            connection.close();
        }
        return paramInfo;
    }
    
    private int getJdbcType(int paramMode, int dataType, String typeName) {
        switch (paramMode) {
            case 1: return Types.IN;
            case 2: return Types.OUT;
            case 3: return Types.INOUT;
            default: return Types.NULL;
        }
    }
}


在MyBatis的mapper.xml文件中,可以使用标签遍历上述获取到的参数信息,并动态构建调用存储过程的SQL语句。例如:



<select id="callProcedure" statementType="CALLABLE">
    { call ${procedureName}(
    <foreach collection="params" item="param" separator=",">
        #{param.value, mode=IN, jdbcType=param.type}
    </foreach>
) }
    </select>


在上述示例中,使用标签遍历了params参数Map对象中的所有参数,并动态构建了存储过程的调用语句。其中,${procedureName}为调用存储过程的名称,#{param.value}为当前参数的值,mode和jdbcType属性分别指定了参数的输入/输出类型和JDBC类型。您可以根据实际需求调整这些参数。

最后,在Java代码中调用MyBatis mapper接口的callProcedure方法,将上述动态构建的SQL语句作为参数传递给该方法,即可执行存储过程并获取结果。例如:


SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    ProcedureParamMapper mapper = sqlSession.getMapper(ProcedureParamMapper.class);
    Map<String, Object> params = mapper.getProcedureParams("bsp_CommRole");
    Map<String, Object> inputParams = new HashMap<>();
    inputParams.put("action", "someAction");
    inputParams.put("lan_id", 1);
    inputParams.put("branch_id", "someBranchId");
    Map<String, Object> outputParams = new HashMap<>();
    outputParams.put("column_title", null);
    outputParams.put("column_length", null);
    outputParams.put("column_align", null);
    Map<String, Object> result = new HashMap<>();
    result.putAll(inputParams);
    result.putAll(outputParams);
    sqlSession.selectOne("callProcedure", new HashMap<String, Object>() {{
        put("procedureName", "bsp_CommRole");
        put("params", result);
    }});
    System.out.println("output parameters: " + outputParams);
} finally {
    sqlSession.close();
}


在上述示例中,首先调用ProcedureParamMapper的getProcedureParams方法获取存储过程的最新参数信息,并将输入参数和输出参数分别存储在inputParams和outputParams Map对象中。然后,将inputParams和outputParams合并为一个result Map对象,并将procedureName和result对象作为参数传递给callProcedure方法。最后,从outputParams Map对象中获取存储过程的输出参数结果。

参考GPT和自己的思路,根据您的描述,您需要动态调整Mybatis调用存储过程的参数,以适应存储过程参数的修改。下面提供一种基于Mybatis自定义TypeHandler的解决方案。

首先,您需要编写一个自定义的TypeHandler,用于将存储过程的参数和类型转换为Mybatis的参数类型和模式。以下是一个示例TypeHandler:

public class StoredProcedureTypeHandler extends BaseTypeHandler<List<StoredProcedureParam>> {
    
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<StoredProcedureParam> parameter, JdbcType jdbcType) throws SQLException {
        StringBuilder sb = new StringBuilder();
        sb.append("{call ").append(parameter.get(0).getName()).append("(");
        for (int j = 1; j < parameter.size(); j++) {
            sb.append("?,");
        }
        sb.deleteCharAt(sb.length() - 1).append(")}");
        ps.setString(i, sb.toString());
    }

    @Override
    public List<StoredProcedureParam> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return parseStoredProcedureParams(rs.getString(columnName));
    }

    @Override
    public List<StoredProcedureParam> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return parseStoredProcedureParams(rs.getString(columnIndex));
    }

    @Override
    public List<StoredProcedureParam> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return parseStoredProcedureParams(cs.getString(columnIndex));
    }

    private List<StoredProcedureParam> parseStoredProcedureParams(String paramStr) {
        // 解析存储过程参数
        // ...
    }
}

该TypeHandler将存储过程的名称和参数列表转换为Mybatis调用存储过程所需的字符串格式,并将其设置为PreparedStatement的参数。此外,它还将ResultSet返回的存储过程参数字符串转换为Java对象。您需要根据实际情况实现parseStoredProcedureParams方法。

接下来,您需要在Mybatis的配置文件中注册该TypeHandler:

<typeHandlers>
    <typeHandler handler="com.example.StoredProcedureTypeHandler" javaType="java.util.List" jdbcType="VARCHAR"/>
</typeHandlers>

在Mapper XML文件中,您需要声明一个返回类型为List的select语句,用于查询存储过程的参数列表:

<select id="getStoredProcedureParams" resultType="java.util.List">
    SELECT param_name, param_type, param_mode
    FROM [sys].[sp_procedure_params_100_managed]
    WHERE procedure_name = #{procedureName}
    ORDER BY param_order;
</select>

最后,在Java代码中,您可以使用SqlSession的selectOne方法查询存储过程的参数列表,然后将其作为参数传递给Mybatis的调用存储过程方法。以下是一个示例:

SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    List<StoredProcedureParam> params = sqlSession.selectOne("com.example.MyMapper.getStoredProcedureParams", "存储过程名");
    Map<String, Object> paramValues = new HashMap<>();
    for (StoredProcedureParam param : params) {
        if (param.getMode() == StoredProcedureParam.Mode.IN) {
            paramValues.put(param.getName(), /* 从请求参数中获取对应的值 */);
} else {
paramValues.put(param.getName(), null);
// 输出参数需要预先设为 null,否则会抛出异常
}
}
sqlSession.selectOne("com.example.MyMapper.callStoredProcedure", new StoredProcedureCall("存储过程名", paramValues));

// 处理输出参数
for (StoredProcedureParam param : params) {
    if (param.getMode() == StoredProcedureParam.Mode.OUT) {
        System.out.println(param.getName() + ": " + paramValues.get(param.getName()));
    }
}
} finally {
sqlSession.close();
}

其中,"com.example.MyMapper.getStoredProcedureParams" 和 "com.example.MyMapper.callStoredProcedure" 是两个 MyBatis 的 Mapper 方法,需要在对应的 Mapper XML 文件中定义它们的 SQL 查询和调用。其中,getStoredProcedureParams 方法返回指定存储过程的参数信息,callStoredProcedure 方法调用指定的存储过程。

在 getStoredProcedureParams 方法中,可以使用数据库的元数据功能获取指定存储过程的参数信息。例如,对于 Oracle 数据库,可以使用如下 SQL 查询:

SELECT
argument_name AS name,
data_type AS type,
IN_OUT AS mode
FROM
all_arguments
WHERE
object_name = #{procedureName}
ORDER BY
position;

这个查询会返回存储过程的参数信息,包括参数名、参数类型和参数模式(输入还是输出)等。可以使用 MyBatis 的 resultMap 和 resultType 等功能将这些信息映射为 Java 对象。

在 callStoredProcedure 方法中,可以使用 MyBatis 的 selectOne 或者 update 等方法来执行存储过程的调用。例如,对于 Oracle 数据库,可以使用如下语句调用存储过程:

BEGIN
存储过程名(:param1, :param2, ..., :paramN);
END;

其中,:param1, :param2, ..., :paramN 是存储过程的参数,需要和 getStoredProcedureParams 方法中查询到的参数名一一对应。这里使用了 BEGIN 和 END 来封装存储过程的调用,这是因为在 Oracle 中,存储过程调用必须在 BEGIN 和 END 中进行封装。

在调用存储过程后,可以从 paramValues 中获取输出参数的值,并进行相应的处理。需要注意的是,输出参数需要预先设为 null,否则会抛出异常。

看看这样能不能达到你的要求,仅供参考:
通用方法获取存储过程最新的参数和顺序:

public Map<String, String> getProcedureParams(String procedureName) {
    Map<String, String> params = new HashMap<>();
    // 调用存储过程获取最新参数信息,并将结果保存到params中
    return params;
}

mapper.xml中的select语句:

<select id="getProcedureParams" resultType="java.util.HashMap">
  SELECT PARAMETER_NAME AS paramName, DATA_TYPE AS jdbcType
  FROM [sys].[sp_procedure_params_100_managed]
  WHERE PROCEDURE_NAME = #{procedureName}
</select>

mapper.xml中的动态SQL:

<sql id="procedureParams">
  <foreach collection="params" item="value" separator=",">
    #{value, mode=IN, jdbcType=${paramMap[value]}}
  </foreach>
</sql>

mapper接口中的方法:

public void callProcedure() {
    // 调用select语句获取存储过程最新的参数和顺序
    Map<String, String> paramMap = sqlSession.selectOne("getProcedureParams", "存储过程名");

    // 使用动态SQL生成call语句的参数部分
    String paramsSql = sqlSession.getConfiguration().getMappedStatement("procedureParams").getSqlSource().getBoundSql(null).getSql();

    // 拼接call语句并执行
    String callSql = "CALL 存储过程名(" + paramsSql + ")";
    sqlSession.selectOne(callSql);
}

1楼上的可以的。

定义多个存储过程呢,然后根据条件判断该去调用某一个存储过程

分三步走
第一步:获取到最新的参数字段和顺序,用新定义的实体类接收
第二步:在java代码中将新的参数字段和前端的传的值一一对应
第三步:将第二步的字段以及值和bsp_CommRole;8存储过程中的参数一一对应上。