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-3个参数):
// 优先选择@Param User getByIdAndStatus(@Param("id") Long id, @Param("status") Integer status);
中等复杂度查询(4-7个参数):
// 使用JavaBean List<User> findByQuery(UserQuery query);
复杂查询(条件+分页+排序):
// 混合模式最佳 PageResult<User> search( @Param("query") UserQuery query, @Param("page") PageParam page, @Param("sort") SortParam sort );
高度动态查询:
// 不得已时使用Map List<User> dynamicQuery(Map<String, Object> params);
绝对避免:
// 不要使用按参数顺序的方式 List<User> findByParams(Integer param1, String param2); // 难以维护
通过以上代码对比,可以清楚地看到每种方式的适用场景和优缺点。在实际项目中,建议团队统一规范,根据查询复杂度选择最合适的方式,通常以@Param和JavaBean为主,混合模式为辅。