多个接口参数的用法

通过观察,不难发现目前所列举的接口中方法的参数只有一个,参数的类型可以分为两种:一种是基本类型,另一种是 JavaBean。

当参数是一个基本类型的时候,它在 XML 文件中对应的 SQL 语句只会使用一个参数,例如 delete 方法。当参数是一个 JavaBean 类型的时候,它在 XML 文件中对应的 SQL 语句会有多个参数,例如 insert、update 方法。

在实际应用中经常会遇到使用多个参数的情况。前面几节的例子中,我们将多个参数合并到一个 JavaBean 中,并使用这个 JavaBean 作为接口方法的参数。这种方法用起来很方便,但并不适合全部的情况,因为不能只为了两三个参数去创建新的 JavaBean 类,因此对于参数比较少的情况,还有两种方式可以采用:使用 Map 类型作为参数或使用 @Param 注解。

使用 Map 类型作为参数的方法,就是在 Map 中通过 key 来映射 XML 中 SQL 使用的参数值名字,value 用来存放参数值,需要多个参数时,通过 Map 的 key-value 方式传递参数值,由于这种方式还需要自己手动创建 Map 以及对参数进行赋值,其实并不简洁,所以对这种方式只做以上简单介绍,接下来着重讲解使用 @Param 注解的方式。

先来看一下,如果在接口中使用多个参数但不使用 @Param 注解会发生什么错误。现在要根据用户 id 和角色的 enabled 状态来查询用户的所有角色,定义一个接口方法,代码如下。

/**
 * 根据用户ID获取角色信息
 * @param userId 用户ID
 * @param enabled 有效标志
 * @return 角色列表
 */
List<SysRole> selectRolesByUserIdAndRoleEnabled(Long userId,Integer enabled);

这个接口方法对应的 UserMapper.xml 中的代码如下。

<select id="selectRolesByUserIdAndRoleEnabled" resultType="cn.liaozh.mybatis2.ch2.xml.model.SysRole">
    select
        r.id,
        r.role_name roleName,
        r.enabled,
        r.create_by createBy,
        r.create_time createTime
    from sys_user u
    inner join sys_user_role ur on u.id = ur.user_id
    inner join sys_role r on ur.role_id = r.id
    where u.id = #{userId} and r.enabled = #{enabled}
</select>

在 UserMapperTest 中添加如下代码进行测试。

@Test
public void testSelectRolesByUserIdAndRoleEnabledDeleteById(){
    SqlSession sqlSession = getSqlSession();
    try {
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 调用 SelectRolesByUserIdAndRoleEnabled 方法查询用户的角色
        List<SysRole> roleList = userMapper.selectRolesByUserIdAndRoleEnabled(1L,1);
        // 结果不为空
        Assert.assertNotNull(roleList);
        // 角色数量大于0个
        Assert.assertTrue(roleList.size() > 0);
    }finally {
        // 不要忘记关闭 sqlSession
        sqlSession.close();
    }
}

测试代码输出日志会显示如下错误。

image 2024 05 21 17 00 56 741

这个错误表示,XML 可用的参数只有 0、1、param1、param2,没有 userId。0 和 1,param1 和 param2 都是 MyBatis 根据参数位置自定义的名字,这时如果将 XML 中的 #{userId} 改为 #{0}#{param1},将 #{enabled} 改为 #{1}#{param2},这个方法就可以被正常调用了。这样讲只是为了让大家理解它们之间的关系,但实际上并不建议这么做。

现在,在接口方法的参数前添加 @Param 注解,代码如下。

/**
 * 根据用户ID获取角色信息
 * @param userId 用户ID
 * @param enabled 有效标志
 * @return 角色列表
 */
List<SysRole> selectRolesByUserIdAndRoleEnabled(@Param("userId") Long userId,@Param("enabled")Integer enabled);

再次调用上面的测试,输出日志如下。

测试通过了,这时的 XML 文件中对应的 SQL 的可用参数变成了 [userId,enabled,param1,param2],如果把 #{userId} 改为 #{param1},把 #{enabled} 改为 #{param2},测试也可以通过。

给参数配置 @Param 注解后,MyBatis 就会自动将参数封装成 Map 类型,@Param 注解值会作为 Map 中的 key,因此在 SQL 部分就可以通过配置的注解值来使用参数。

到这里大家可能会有一个疑问:当只有一个参数(基本类型或拥有 TypeHandler 配置的类型)的时候,为什么可以不使用注解?这是因为在这种情况下(除集合和数组外),MyBatis 不关心这个参数叫什么名字就会直接把这个唯一的参数值拿来使用。

以上是参数类型比较简单时使用 @Param 注解的例子,当参数类型是一些 JavaBean 的时候,用法略有不同。将接口方法中的参数换成 JavaBean 类型,代码如下。

/**
 * 根据用户ID获取角色信息
 * @param user
 * @param role
 * @return 角色列表
 */
List<SysRole> selectRolesByUserIdAndRoleEnabled(@Param("userId")SysUser user,@Param("enabled")SysRole role);

这时,在 XML 中就不能直接使用 #{userId}#{enabled} 了,而是要通过点取值方式使用 #{user.id}#{role.enabled} 从两个 JavaBean 中取出指定属性的值。修改好对应的 XML 文件后,大家可以自行完善代码并进行测试。

除了以上常用的参数类型外,接口的参数还可能是集合或者数组。本章还不会涉及这方面的用法,有关集合和数组的用法可以阅读4.4节的内容提前了解。