MyBatis多参数传递方式

发布于:2025-05-15 ⋅ 阅读:(12) ⋅ 点赞:(0)

MyBatis多参数传递方式优缺点详解(附代码对比)

1. Map传递参数方式

代码示例

// Service层
public List<User> findUsers(Map<String, Object> params) {
    return userMapper.findByMap(params);
}

// Controller层
Map<String, Object> params = new HashMap<>();
params.put("name", "张");
params.put("status", 1);
params.put("minAge", 18);
List<User> users = userService.findUsers(params);
<!-- Mapper XML -->
<select id="findByMap" parameterType="map" resultType="User">
    SELECT * FROM users
    WHERE name LIKE CONCAT('%', #{name}, '%')
    AND status = #{status}
    <if test="minAge != null">
      AND age >= #{minAge}
    </if>
</select>

优点

  • 动态扩展性强:可以随时添加新参数而不改变接口
// 可随时添加新条件而不影响原有代码
params.put("maxAge", 30);
params.put("department", "技术部");
  • 适合不确定参数的查询:如高级搜索功能

缺点

  • 类型不安全示例
// 编译通过但运行时报错
params.put("status", "active");  // 应该是数字但传了字符串
  • 重构困难:重命名参数时IDE无法识别字符串key
// 修改了map的key但忘记修改XML中的#{}引用
params.put("userStatus", 1);  // 原来是status

2. @Param注解方式(推荐)

代码示例

// Mapper接口
public interface UserMapper {
    List<User> findByConditions(
        @Param("name") String name,
        @Param("status") Integer status,
        @Param("minAge") Integer minAge,
        @Param("roleIds") List<Integer> roleIds
    );
}
<!-- Mapper XML -->
<select id="findByConditions" resultType="User">
    SELECT * FROM users
    WHERE name LIKE CONCAT('%', #{name}, '%')
    AND status = #{status}
    <if test="minAge != null">
      AND age >= #{minAge}
    </if>
    <if test="roleIds != null and roleIds.size() > 0">
      AND role_id IN
      <foreach collection="roleIds" item="roleId" open="(" separator="," close=")">
          #{roleId}
      </foreach>
    </if>
</select>

优点

  • 类型安全示例
// 编译时会检查类型是否匹配
List<User> findByConditions(
    @Param("name") String name,      // 明确类型
    @Param("status") Integer status // 只能是Integer
);
  • IDE支持良好
    • 自动补全参数名
    • 重命名参数自动更新XML引用
    • 点击跳转到XML定义

缺点

  • 参数过多时的问题
// 当参数超过5个时,方法签名变得冗长
List<User> findUsers(
    @Param("name") String name,
    @Param("status") Integer status,
    @Param("minAge") Integer minAge,
    @Param("maxAge") Integer maxAge,
    @Param("department") String department,
    @Param("joinDateFrom") Date joinDateFrom,
    @Param("joinDateTo") Date joinDateTo
    // 更多参数...
);

3. JavaBean对象方式

代码示例

// 参数对象
@Data  // Lombok注解,自动生成getter/setter
public class UserQuery {
    private String name;
    private Integer status;
    private Integer minAge;
    private Integer maxAge;
    private List<Integer> roleIds;
    private Date createTimeStart;
    private Date createTimeEnd;
}

// Mapper接口
public interface UserMapper {
    List<User> findByQuery(UserQuery query);
}
<!-- Mapper XML -->
<select id="findByQuery" parameterType="com.example.UserQuery" resultType="User">
    SELECT * FROM users
    <where>
        <if test="name != null and name != ''">
          AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="status != null">
          AND status = #{status}
        </if>
        <if test="minAge != null">
          AND age >= #{minAge}
        </if>
        <if test="roleIds != null and roleIds.size() > 0">
          AND role_id IN
          <foreach collection="roleIds" item="roleId" open="(" separator="," close=")">
              #{roleId}
          </foreach>
        </if>
    </where>
</select>

优点

  • 维护性示例
// 新增参数只需在UserQuery中添加字段,不影响接口签名
public class UserQuery {
    // 原有字段...
    private String email; // 新增字段
}

// Mapper接口无需修改
List<User> findByQuery(UserQuery query);
  • 验证逻辑集中
public class UserQuery {
    @NotNull
    private Integer status;
    
    @Min(0)
    @Max(150)
    private Integer minAge;
    
    // 自定义验证逻辑
    public boolean isValidDateRange() {
        return createTimeStart == null || createTimeEnd == null 
               || createTimeStart.before(createTimeEnd);
    }
}

缺点

  • 简单查询显得臃肿
// 对于只需要1-2个参数的查询
UserQuery query = new UserQuery();
query.setName("张");
query.setStatus(1);
// 需要创建对象并设置属性,不如直接传参简洁

4. 混合使用@Param和JavaBean

代码示例

// 分页参数对象
@Data
public class PageParam {
    private int pageNum;
    private int pageSize;
}

// Mapper接口
public interface UserMapper {
    List<User> search(
        @Param("query") UserQuery query,
        @Param("page") PageParam page
    );
}
<!-- Mapper XML -->
<select id="search" resultType="User">
    SELECT * FROM users
    <where>
        <include refid="queryConditions"/>
    </where>
    LIMIT #{page.pageSize} OFFSET #{page.pageSize * (page.pageNum - 1)}
</select>

<sql id="queryConditions">
    <if test="query.name != null">
      AND name LIKE CONCAT('%', #{query.name}, '%')
    </if>
    <!-- 其他条件 -->
</sql>

优点

  • 职责分离清晰

    • query对象负责查询条件
    • page对象负责分页逻辑
    • 排序参数可以再添加@Param("sort") SortParam
  • 复用性强

<!-- 其他查询可以复用相同的查询条件 -->
<select id="countByQuery" resultType="int">
    SELECT COUNT(*) FROM users
    <where>
        <include refid="queryConditions"/>
    </where>
</select>

终极选择建议

  1. 简单查询(1-3个参数)

    // 优先选择@Param
    User getByIdAndStatus(@Param("id") Long id, @Param("status") Integer status);
    
  2. 中等复杂度查询(4-7个参数)

    // 使用JavaBean
    List<User> findByQuery(UserQuery query);
    
  3. 复杂查询(条件+分页+排序)

    // 混合模式最佳
    PageResult<User> search(
        @Param("query") UserQuery query,
        @Param("page") PageParam page,
        @Param("sort") SortParam sort
    );
    
  4. 高度动态查询

    // 不得已时使用Map
    List<User> dynamicQuery(Map<String, Object> params);
    
  5. 绝对避免

    // 不要使用按参数顺序的方式
    List<User> findByParams(Integer param1, String param2);  // 难以维护
    

通过以上代码对比,可以清楚地看到每种方式的适用场景和优缺点。在实际项目中,建议团队统一规范,根据查询复杂度选择最合适的方式,通常以@Param和JavaBean为主,混合模式为辅。