MyBatis?枚舉映射的實現(xiàn)示例
一、MyBatis 枚舉映射四大實現(xiàn)方案
1. 基礎(chǔ)序數(shù)映射(EnumTypeHandler)
// Java 枚舉類 public enum OrderStatus { NEW, PAID, DELIVERED, CANCELLED; } // MyBatis實體類映射 public class Order { private OrderStatus status; // getters/setters }
??Mapper XML 配置??:
<resultMap id="orderResultMap" type="Order"> <result property="status" column="status"/> </resultMap>
??MySQL 表設(shè)計??:
CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, status TINYINT COMMENT '0-新訂單,1-已支付,2-已發(fā)貨,3-已取消' );
??優(yōu)缺點??:
- ? 零配置自動生效
- ?? 無法修改枚舉順序
- ?? 數(shù)據(jù)庫可讀性差
2. 枚舉名稱映射(EnumOrdinalTypeHandler)
// MyBatis 配置文件 <typeHandlers> <typeHandler handler="org.apache.ibatis.type.EnumTypeHandler" javaType="com.example.OrderStatus"/> </typeHandlers> // 或全局配置 <settings> <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/> </settings>
??表設(shè)計??:
ALTER TABLE orders MODIFY COLUMN status VARCHAR(20) COMMENT '訂單狀態(tài)';
??適配枚舉改動??:
public enum OrderStatus { NEW("新訂單"), PAID("已支付"), DELIVERED("已發(fā)貨"), CANCELLED("已取消"); private final String desc; OrderStatus(String desc) { this.desc = desc; } }
3. 自定義類型處理器(TypeHandler)
3.1 基于字符串的轉(zhuǎn)換
@MappedTypes(OrderStatus.class) public class OrderStatusHandler extends BaseTypeHandler<OrderStatus> { @Override public void setNonNullParameter(PreparedStatement ps, int i, OrderStatus status, JdbcType jdbcType) { ps.setString(i, status.name()); } @Override public OrderStatus getNullableResult(ResultSet rs, String columnName) { String code = rs.getString(columnName); return OrderStatus.valueOf(code); } // 其他getNullableResult方法... }
3.2 基于編碼的轉(zhuǎn)換(推薦)
@MappedTypes(UserType.class) public class UserTypeHandler extends BaseTypeHandler<UserType> { @Override public void setNonNullParameter(PreparedStatement ps, int i, UserType userType, JdbcType jdbcType) { ps.setString(i, userType.getCode()); } @Override public UserType getNullableResult(ResultSet rs, String columnName) { String code = rs.getString(columnName); return UserType.fromCode(code); } }
??注冊處理器??:
<typeHandlers> <typeHandler handler="com.example.handler.UserTypeHandler"/> </typeHandlers>
??實體類使用??:
public class User { @TableField(typeHandler = UserTypeHandler.class) private UserType type; }
4. 枚舉值關(guān)聯(lián)表方案
CREATE TABLE user_types ( id TINYINT PRIMARY KEY AUTO_INCREMENT, code CHAR(2) UNIQUE NOT NULL, name VARCHAR(20) NOT NULL ); INSERT INTO user_types (code, name) VALUES ('A', '管理員'), ('E', '編輯'), ('U', '普通用戶');
??Mapper XML 查詢??:
<resultMap id="userResultMap" type="User"> <result property="id" column="id"/> <association property="type" column="type_code" select="selectUserTypeByCode"/> </resultMap> <select id="selectUserTypeByCode" resultType="UserType"> SELECT code, name AS description FROM user_types WHERE code = #[code] </select>
二、MyBatis-Plus 高級枚舉映射
1. 內(nèi)置枚舉處理器
public enum ProductStatus implements IEnum<Integer> { DRAFT(0), PUBLISHED(1), ARCHIVED(2); private final int value; ProductStatus(int value) { this.value = value; } @Override public Integer getValue() { return this.value; } } // 實體類使用 public class Product { private ProductStatus status; }
??全局配置(application.yml)??:
mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
2. 字段注解映射
public class Product { @TableField(value = "status", typeHandler = ProductStatusHandler.class) private ProductStatus status; }
3. JSON格式存儲枚舉屬性
public class Order { @TableField(typeHandler = JsonTypeHandler.class) private Map<OrderStatus, Integer> statusStatistics; }
??JSON存儲結(jié)構(gòu)??:
{ "NEW": 10, "PAID": 5, "DELIVERED": 3 }
三、枚舉映射最佳實踐方案
1. 防御式枚舉設(shè)計
public enum OrderStatus { NEW("N"), PAID("P"), DELIVERED("D"), CANCELLED("C"); private final String code; private static final Map<String, OrderStatus> CODE_MAP = new HashMap<>(); static { for (OrderStatus status : values()) { CODE_MAP.put(status.code, status); } } public static OrderStatus fromCode(String code) { OrderStatus status = CODE_MAP.get(code); if (status == null) { if (code == null) return null; String cleanCode = code.trim().toUpperCase(); return Optional.ofNullable(CODE_MAP.get(cleanCode)) .orElseThrow(() -> new IllegalArgumentException( "無效狀態(tài)碼: " + code)); } return status; } }
2. 枚舉基類設(shè)計
public interface EnumCode { String getCode(); String getDescription(); } public abstract class BaseEnumHandler<E extends Enum<E> & EnumCode> extends BaseTypeHandler<E> { private final Class<E> type; private final Map<String, E> enumMap; public BaseEnumHandler(Class<E> type) { this.type = type; this.enumMap = Arrays.stream(type.getEnumConstants()) .collect(Collectors.toMap(EnumCode::getCode, e -> e)); } @Override public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) { ps.setString(i, parameter.getCode()); } @Override public E getNullableResult(ResultSet rs, String columnName) { String code = rs.getString(columnName); return code == null ? null : enumMap.get(code); } }
四、實戰(zhàn)應(yīng)用場景
1. 多狀態(tài)組合(BIT存儲)
public enum Permission { READ(1), WRITE(2), DELETE(4), EXECUTE(8); private final int bitValue; // 存儲所有權(quán)限值到單個整數(shù) public static int encode(Set<Permission> permissions) { return permissions.stream() .mapToInt(p -> p.bitValue) .sum(); } public static Set<Permission> decode(int value) { return Arrays.stream(values()) .filter(p -> (value & p.bitValue) != 0) .collect(Collectors.toSet()); } } // MyBatis類型處理器 public class PermissionSetHandler extends BaseTypeHandler<Set<Permission>> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Set<Permission> perms, JdbcType jdbcType) { ps.setInt(i, Permission.encode(perms)); } @Override public Set<Permission> getNullableResult(ResultSet rs, String columnName) { int value = rs.getInt(columnName); return rs.wasNull() ? Collections.emptySet() : Permission.decode(value); } }
2. 動態(tài)狀態(tài)機驗證
public class OrderService { @Transactional public void updateOrderStatus(Long orderId, OrderStatus newStatus) { Order order = orderMapper.selectById(orderId); OrderStatus oldStatus = order.getStatus(); if (!oldStatus.isAllowedTransition(newStatus)) { throw new IllegalStateException("狀態(tài)轉(zhuǎn)換非法: " + oldStatus + " -> " + newStatus); } order.setStatus(newStatus); orderMapper.updateById(order); } } // 枚舉中定義狀態(tài)轉(zhuǎn)移規(guī)則 public enum OrderStatus { NEW { @Override public boolean isAllowedTransition(OrderStatus newStatus) { return newStatus == PAID || newStatus == CANCELLED; } }, // 其他狀態(tài)定義... }
五、性能優(yōu)化技巧
??靜態(tài)映射緩存??:
public abstract class CachedEnumHandler<E extends Enum<E> & EnumCode> extends BaseTypeHandler<E> { private final Map<String, E> codeEnumMap; private final Map<E, String> enumCodeMap; public CachedEnumHandler(Class<E> enumClass) { E[] enums = enumClass.getEnumConstants(); codeEnumMap = Arrays.stream(enums) .collect(Collectors.toMap(EnumCode::getCode, e -> e)); enumCodeMap = Arrays.stream(enums) .collect(Collectors.toMap(e -> e, EnumCode::getCode)); } }
??批量處理優(yōu)化??:
@Mapper public interface BatchMapper { @Insert("<script>" + "INSERT INTO users (type, name) VALUES " + "<foreach item='item' collection='list' separator=','>" + "(#{item.type, typeHandler=com.example.UserTypeHandler}, #{item.name})" + "</foreach>" + "</script>") int batchInsertUsers(@Param("list") List<User> users); }
??數(shù)據(jù)庫約束優(yōu)化??:
-- MySQL ENUM 類型約束 status ENUM('NEW','PAID','DELIVERED','CANCELLED') NOT NULL DEFAULT 'NEW' COMMENT '訂單狀態(tài)' -- PostgreSQL檢查約束 ALTER TABLE orders ADD CONSTRAINT status_check CHECK (status IN ('NEW', 'PAID', 'DELIVERED', 'CANCELLED'));
六、錯誤解決方案手冊
1. 序列化問題修復(fù)
// 添加枚舉序列化配置 @JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum OrderStatus implements EnumCode { // ... } // 或自定義序列化器 public class EnumCodeSerializer extends StdSerializer<EnumCode> { public EnumCodeSerializer() { super(EnumCode.class); } @Override public void serialize(EnumCode value, JsonGenerator gen, SerializerProvider provider) { gen.writeStartObject(); gen.writeStringField("code", value.getCode()); gen.writeStringField("description", value.getDescription()); gen.writeEndObject(); } }
2. 國際化方案
public interface LocalizedEnum { String getCode(); default String getMessage(Locale locale) { ResourceBundle bundle = ResourceBundle.getBundle( "enum_messages", locale); return bundle.getString(this.getClass().getSimpleName() + "." + getCode()); } } // 多語言資源文件 // en_US.properties UserType.A=Administrator UserType.E=Editor UserType.U=User // zh_CN.properties UserType.A=管理員 UserType.E=編輯 UserType.U=普通用戶
總結(jié):MyBatis枚舉映射決策樹
graph TD A[需要映射枚舉] --> B{是否簡單狀態(tài)值?} B --> |是| C{是否確定永不修改順序?} C --> |是| D[使用EnumTypeHandler默認(rèn)序數(shù)映射] C --> |否| E[使用EnumOrdinalTypeHandler名稱映射] B --> |否| F{是否需要業(yè)務(wù)編碼?} F --> |是| G[自定義TypeHandler+編碼設(shè)計] F --> |否| H{是否多語言/復(fù)雜屬性?} H --> |是| I[關(guān)聯(lián)表映射方案] H --> |否| J[直接使用MyBatis-Plus枚舉方案]
??企業(yè)級應(yīng)用建議??:
- 選擇??自定義編碼映射??作為默認(rèn)方案
- 對性能敏感的常量枚舉使用??序數(shù)映射??
- 需要多語言支持的使用??關(guān)聯(lián)表映射??
- 組合狀態(tài)使用??BIT存儲+解碼方案??
遵循這些模式,可構(gòu)建出健壯且易維護的枚舉持久化層,完美平衡數(shù)據(jù)庫高效存儲與業(yè)務(wù)代碼的可讀性需求。
到此這篇關(guān)于MyBatis 枚舉映射的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)MyBatis 枚舉映射內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA集成DeepSeek的詳細(xì)教程(保姆級教程)
DeepSeek作為一款強大的代碼搜索和分析工具,能夠幫助開發(fā)者快速定位代碼、理解項目結(jié)構(gòu)以及優(yōu)化代碼質(zhì)量,本文將詳細(xì)介紹如何在IntelliJ?IDEA中集成DeepSeek,并展示如何利用它來提升開發(fā)效率,感興趣的朋友一起看看吧2025-02-02springboot如何接收復(fù)雜參數(shù)(同時接收J(rèn)SON與文件)
文章介紹了在Spring Boot中同時處理JSON和文件上傳時使用`@RequestPart`注解的方法,`@RequestPart`可以接收多種格式的參數(shù),包括JSON和文件,并且可以作為`multipart/form-data`格式中的key2025-02-02MyBatis使用自定義TypeHandler轉(zhuǎn)換類型的實現(xiàn)方法
這篇文章主要介紹了MyBatis使用自定義TypeHandler轉(zhuǎn)換類型的實現(xiàn)方法,本文介紹使用TypeHandler 實現(xiàn)日期類型的轉(zhuǎn)換,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-10-10java 配置MyEclipse Maven環(huán)境具體實現(xiàn)步驟
這篇文章主要介紹了 java 配置MyEclipse Maven環(huán)境具體實現(xiàn)步驟的相關(guān)資料,具有一定的參考價值,需要的朋友可以參考下2016-11-11Java?KeyGenerator.generateKey的19個方法代碼示例
在下文中一共展示了KeyGenerator.generateKey方法的19個代碼示例,這些例子默認(rèn)根據(jù)受歡迎程度排序2021-12-12