枚举定义 ReagentStatus.java
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
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
java
// 通过状态,查询试剂列表
List<Reagent> selectByStatus(ReagentQueryDTO queryDTO, String userName);
ReagentMapper.xml
调用枚举的 name()
方法获取枚举常量名称字符串
XML
<!-- 通过状态,查询试剂列表 -->
<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 1
from Employee
where 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'
(枚举的字符串名称)
- 实际执行的SQL:
方法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
(枚举的序号值)
- 实际执行的SQL:
方法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> { @Override public void setParameter(PreparedStatement ps, int i, UserStatus status, JdbcType jdbcType) throws SQLException { ps.setString(i, status.getCode()); // 存入枚举的code值 } @Override public 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; }
@Override
public 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() == '常量名'
的方式,兼具可读性和类型安全性。