Java + Spring Boot + MyBatis 枚举变量传递给XML映射文件做判断

枚举定义 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 &lt; #{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。

示例代码
  1. 枚举定义

    java

    复制

    下载

    复制代码
    public enum UserStatus {
        ACTIVE, INACTIVE, PENDING
    }
  2. Mapper接口

    java

    复制

    下载

    复制代码
    public interface UserMapper {
        List<User> getUsersByStatus(@Param("status") UserStatus status);
    }
  3. XML映射文件

    xml

    复制

    下载

    运行

    复制代码
    <select id="getUsersByStatus" resultType="User">
        SELECT * FROM users WHERE status = #{status}
        <!-- 相当于 #{status, javaType=UserStatus} -->
    </select>
    • 实际执行的SQL:status = 'ACTIVE'(枚举的字符串名称)

方法2:使用枚举序号(ordinal)

通过EnumOrdinalTypeHandler,将枚举的**序号(ordinal())**作为整数传递。

步骤
  1. 全局配置(推荐在mybatis-config.xml中)

    xml

    复制

    下载

    运行

    复制代码
    <typeHandlers>
        <typeHandler 
            handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" 
            javaType="com.example.UserStatus"/> <!-- 替换为你的枚举全限定名 -->
    </typeHandlers>
  2. 或局部配置(在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接口。

示例:将枚举的自定义编码存入数据库
  1. 枚举定义(添加自定义字段)

    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; }
    }
  2. 自定义类型处理器

    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方法实现类似...
    }
  3. 注册类型处理器

    • 全局注册(在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>
  4. Mapper接口保持不变

    java

    复制

    下载

    复制代码
    List<User> getUsersByStatus(@Param("status") UserStatus status);

关键点总结

方法 传递的值 配置方式 适用场景
默认EnumTypeHandler 枚举名称(字符串) 无需配置 直接存储枚举名称到VARCHAR列
EnumOrdinalTypeHandler 枚举序号(整数) 全局配置或局部指定 存储序号到INT列
自定义TypeHandler 任意自定义属性 实现接口并注册 复杂映射(如编码转换)

常见问题解决

  1. 枚举传递后值为null

    • 检查类型处理器是否匹配。

    • 确保XML中的参数名与@Param一致。

  2. 数据库存的是整数但枚举是字符串

    • 使用EnumOrdinalTypeHandler或自定义处理器。
  3. 自定义处理器不生效

    • 确认是否在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>

重要提醒

  1. 空值检查 :始终优先判断 queryDTO.status != null

  2. 大小写敏感name() 返回的枚举名称是大小写敏感的

  3. 避免魔法值:考虑使用常量替代硬编码字符串

    xml

    复制

    下载

    运行

    复制代码
    <when test="queryDTO.status.name() == T(com.example.Constants).USER_REGULAR">

根据你的实际需求,推荐使用 枚举.name() == '常量名' 的方式,兼具可读性和类型安全性。

相关推荐
盛夏绽放4 分钟前
Python 目录操作详解
java·服务器·python
贰拾wan5 分钟前
ArrayList源码分析
java·数据结构
Code季风6 分钟前
跨语言RPC:使用Java客户端调用Go服务端的JSON-RPC服务
java·网络协议·rpc·golang·json
豆沙沙包?31 分钟前
2025年- H82-Lc190--322.零钱兑换(动态规划)--Java版
java·算法·动态规划
都叫我大帅哥1 小时前
背压(Backpressure):响应式编程的“流量控制艺术”
java·flux
浮游本尊1 小时前
Java学习第5天 - 输入输出与字符串处理
java
阿杰学编程1 小时前
Go 语言中的条件判断和for 循环
java·数据库·golang
考虑考虑1 小时前
JDK9中的takeWhile
java·后端·java ee
boy快快长大2 小时前
【CompletableFuture】常用方法(三)
java