Java + Spring Boot + MyBatis 枚举变量传递给XML映射文件做判断
枚举定义 ReagentStatus.java
package com.weiyu.utils.enums;import lombok.Getter;/*** 试剂状态枚举*/
@Getter
public enum ReagentStatus {// 常规REGULAR,// 少库存LESS_INVENTORY,// 零库存ZERO_INVENTORY,// 将过期WILL_EXPIRE,// 已过期EXPIRED,// 已注销LOGGED,// 全部ALL
}
查询对象 DTO ReagentQueryDTO.java
package com.weiyu.pojo;import com.weiyu.utils.enums.ReagentStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;/*** 试剂管理查询 DTO*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ReagentQueryDTO {// 分页器private PageHelper pageHelper = new PageHelper(1, 20);// 试剂类型private String reagentCategory;// 试剂编号private String reagentNo;// 试剂名称private String reagentName;// 试剂状态private ReagentStatus status;// 当天零晨,用于过滤有效期private LocalDateTime currentStartOfDay;
}
ReagentMapper.java
// 通过状态,查询试剂列表List<Reagent> selectByStatus(ReagentQueryDTO queryDTO, String userName);
ReagentMapper.xml
调用枚举的 name()
方法获取枚举常量名称字符串
<!-- 通过状态,查询试剂列表 --><select id="selectByStatus" resultType="com.weiyu.pojo.Reagent">select<include refid="ReagentAllFields" />from Reagent<where><if test="queryDTO.reagentCategory != null and queryDTO.reagentCategory != ''">and rea_TypeName = #{queryDTO.reagentCategory}</if><if test="queryDTO.reagentName != null and queryDTO.reagentName != ''">and rea_Name like '%' + #{queryDTO.reagentName} + '%'</if><if test="queryDTO.status != null"><choose><!-- 常规 --><when test="queryDTO.status.name() == 'REGULAR'">and rea_State = 0 and rea_Amount > 0</when><!-- 少库存 --><when test="queryDTO.status.name() == 'LESS_INVENTORY'">and rea_State = 0 and rea_Amount between 1 and 3</when><!-- 零库存 --><when test="queryDTO.status.name() == 'ZERO_INVENTORY'">and rea_State = 0 and rea_Amount = 0</when><!-- 将过期 --><when test="queryDTO.status.name() == 'WILL_EXPIRE'">and rea_State = 0 and rea_Amount > 0 and rea_OverdueDate between dateadd(day, -1, getdate()) and dateadd(month, 1, getdate())</when><!-- 已过期 --><when test="queryDTO.status.name() == 'EXPIRED'">and rea_State = 0 and rea_Amount > 0 and rea_OverdueDate < #{queryDTO.currentStartOfDay}</when><!-- 已注销 --><when test="queryDTO.status.name() == 'LOGGED'">and rea_State = 1</when><!-- 全部 --></choose></if>and exists (select 1from Employeewhere emp_ID = #{userName}and emp_DeptID = rea_DeptID)</where></select>
在Java中,将枚举变量从Mapper接口传递到MyBatis的XML文件时,MyBatis默认提供了两种处理枚举的方式,也可以通过自定义类型处理器实现更灵活的控制。以下是详细步骤和示例:
方法1:使用MyBatis内置枚举处理器(默认)
MyBatis默认使用EnumTypeHandler
,将枚举的名称(name())作为字符串传递到XML。
示例代码
-
枚举定义:
java
复制
下载
public enum UserStatus {ACTIVE, INACTIVE, PENDING }
-
Mapper接口:
java
复制
下载
public interface UserMapper {List<User> getUsersByStatus(@Param("status") UserStatus status); }
-
XML映射文件:
xml
复制
下载
运行
<select id="getUsersByStatus" resultType="User">SELECT * FROM users WHERE status = #{status}<!-- 相当于 #{status, javaType=UserStatus} --> </select>
-
实际执行的SQL:
status = 'ACTIVE'
(枚举的字符串名称)
-
方法2:使用枚举序号(ordinal)
通过EnumOrdinalTypeHandler
,将枚举的序号(ordinal())作为整数传递。
步骤
-
全局配置(推荐在
mybatis-config.xml
中):xml
复制
下载
运行
<typeHandlers><typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.example.UserStatus"/> <!-- 替换为你的枚举全限定名 --> </typeHandlers>
-
或局部配置(在XML中指定):
xml
复制
下载
运行
<select id="getUsersByStatus" resultType="User">SELECT * FROM users WHERE status = #{status, typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler} </select>
-
实际执行的SQL:
status = 0
(枚举的序号值)
-
方法3:自定义类型处理器(推荐)
当需要存储枚举的自定义属性(如数据库中的编码)时,需实现TypeHandler
接口。
示例:将枚举的自定义编码存入数据库
-
枚举定义(添加自定义字段):
java
复制
下载
public enum UserStatus {ACTIVE("A"), INACTIVE("I"), PENDING("P");private final String code;UserStatus(String code) { this.code = code; }public String getCode() { return code; } }
-
自定义类型处理器:
java
复制
下载
public class UserStatusTypeHandler implements TypeHandler<UserStatus> {@Overridepublic void setParameter(PreparedStatement ps, int i, UserStatus status, JdbcType jdbcType) throws SQLException {ps.setString(i, status.getCode()); // 存入枚举的code值}@Overridepublic UserStatus getResult(ResultSet rs, String columnName) throws SQLException {String code = rs.getString(columnName);return Arrays.stream(UserStatus.values()).filter(s -> s.getCode().equals(code)).findFirst().orElse(null);}// 其他getResult方法实现类似... }
-
注册类型处理器:
-
全局注册(在
mybatis-config.xml
中):xml
复制
下载
运行
<typeHandlers><typeHandler handler="com.example.handler.UserStatusTypeHandler"/> </typeHandlers>
-
或局部使用(在XML中指定):
xml
复制
下载
运行
<select id="getUsersByStatus" resultType="User">SELECT * FROM users WHERE status = #{status, typeHandler=com.example.handler.UserStatusTypeHandler} </select>
-
-
Mapper接口保持不变:
java
复制
下载
List<User> getUsersByStatus(@Param("status") UserStatus status);
关键点总结
方法 | 传递的值 | 配置方式 | 适用场景 |
---|---|---|---|
默认EnumTypeHandler | 枚举名称(字符串) | 无需配置 | 直接存储枚举名称到VARCHAR列 |
EnumOrdinalTypeHandler | 枚举序号(整数) | 全局配置或局部指定 | 存储序号到INT列 |
自定义TypeHandler | 任意自定义属性 | 实现接口并注册 | 复杂映射(如编码转换) |
常见问题解决
-
枚举传递后值为
null
:-
检查类型处理器是否匹配。
-
确保XML中的参数名与
@Param
一致。
-
-
数据库存的是整数但枚举是字符串:
-
使用
EnumOrdinalTypeHandler
或自定义处理器。
-
-
自定义处理器不生效:
-
确认是否在
mybatis-config.xml
中全局注册,或在XML中局部指定。
-
通过以上方法,你可以灵活地在MyBatis中处理枚举类型的传递和映射。
在 MyBatis 的 XML 动态 SQL 中,直接使用 queryDTO.status == 'REGULAR'
这种写法通常是不正确的,因为枚举对象和字符串是不同类型。以下是正确做法和详细解释:
正确解决方案
1. 比较枚举名称(推荐)
xml
复制
下载
运行
<when test="queryDTO.status != null and queryDTO.status.name() == 'REGULAR'">AND user_type = 1 </when>
-
原理:调用枚举的
name()
方法获取枚举常量名称字符串 -
注意:需先检查
queryDTO.status
是否为null
避免 NPE
2. 直接比较枚举常量(需全限定名)
xml
复制
下载
运行
<when test="queryDTO.status == @com.example.UserStatus@REGULAR">AND user_type = 1 </when>
-
格式:
@包名.枚举类名@枚举常量
-
缺点:路径较长且硬编码,修改枚举位置时需要同步更新
3. 使用枚举的 toString()(需重写方法)
java
复制
下载
// 枚举定义 public enum UserStatus {REGULAR("常规用户");private final String desc;UserStatus(String desc) { this.desc = desc; }@Overridepublic String toString() { return this.desc; // 或 return this.name(); } }
xml
复制
下载
运行
<when test="queryDTO.status != null and queryDTO.status.toString() == '常规用户'">AND user_type = 1 </when>
-
适用场景:需要比较自定义描述而非枚举名称时
-
注意:默认
toString()
返回与name()
相同,重写可实现自定义逻辑
❌ 为什么不推荐直接比较字符串
xml
复制
下载
运行
<!-- 错误示例 --> <when test="queryDTO.status == 'REGULAR'">
-
类型不匹配:左侧
queryDTO.status
是枚举对象,右侧'REGULAR'
是字符串 -
实际比较:相当于
enumObject.equals(String)
,永远返回false
-
OGNL 处理:MyBatis 使用 OGNL 表达式,不会自动转换类型
最佳实践总结
场景 | 推荐写法 | 优点 |
---|---|---|
比较枚举常量名 | queryDTO.status.name() == 'REGULAR' | 直观、无需修改枚举定义 |
需要精确匹配枚举实例 | queryDTO.status == @包名.UserStatus@REGULAR | 直接比较对象引用 |
比较枚举的自定义描述 | queryDTO.status.toString() == '描述文本' | 支持业务友好文本 |
多条件判断 | 搭配 <choose> 使用枚举 name() | 清晰易维护 |
完整示例
xml
复制
下载
运行
<select id="searchUsers" resultType="User">SELECT * FROM users<where><choose><when test="queryDTO.status != null and queryDTO.status.name() == 'REGULAR'">AND user_type = 1</when><when test="queryDTO.status != null and queryDTO.status.name() == 'VIP'">AND user_type = 2</when><otherwise>AND user_type IS NOT NULL</otherwise></choose></where> </select>
重要提醒
-
空值检查:始终优先判断
queryDTO.status != null
-
大小写敏感:
name()
返回的枚举名称是大小写敏感的 -
避免魔法值:考虑使用常量替代硬编码字符串
xml
复制
下载
运行
<when test="queryDTO.status.name() == T(com.example.Constants).USER_REGULAR">
根据你的实际需求,推荐使用 枚举.name() == '常量名'
的方式,兼具可读性和类型安全性。