枚舉定義?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() == '常量名'
?的方式,兼具可讀性和類型安全性。