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

发布于:2025-06-18 ⋅ 阅读:(17) ⋅ 点赞:(0)

枚举定义 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 &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() == '常量名' 的方式,兼具可读性和类型安全性。


网站公告

今日签到

点亮在社区的每一天
去签到