开发者

MyBatis 枚举映射的实现示例

目录
  • 一、MyBATis 枚举映射四大实现方案
    • 1. 基础序数映射(EnumTypeHandler)
    • 2. 枚举名称映射(EnumOrdinalTypeHandler)
    • 3. 自定义类型处理器(TypeHandler)
      • 3.1 基于字符串的转换
      • 3.2 基于编码的转换(推荐)
    • 4. 枚举值关联表方案
    • 二、MyBatis-Plus 高级枚举映射
      • 1. 内置枚举处理器
        • 2. 字段注解映射
          • 3. jsON格式存储枚举属性
          • 三、枚举映射最佳实践方案
            • 1. 防御式枚举设计
              • 2. 枚举基类设计
              • 四、实战应用场景
                • 1. 多状态组合(BIT存储)
                  • 2. 动态状态机验证
                  • 五、性能优化技巧
                    • 六、错误解决方案手册
                      • 1. 序列化问题修复
                        • 2. 国际化方案
                        • 总结:MyBatis枚举映射决策树

                          一、MyBatis 枚举映射四大实现方案

                          1. 基础序数映射(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 表设计​​:

                          CREATE TABLE orders (
                              id INT PRIMARY KEY AUTO_INCREMENT,
                              status TINYINT COMMENT '0-新订单,1-已支付,2-已发货,3-已取消'
                          );

                          ​优缺点​​:

                          • ✅ 零配置自动生效
                          • ⚠️ 无法修改枚举顺序
                          • ⚠️ 数据库可读性差

                          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>

                          ​表设计​​:

                          ALTER TABLE orders 
                          MODIFY COLUMN status VARCHAR(20) COMMENT '订单状态';

                          ​适配枚举改动​​:

                          public enum OrderStatus {
                              NEW("新订单"),
                              PAID("已支付"),
                              DELIVERED("已发货"),
                              CANCELLED("已取消");
                              
                              private final String desc;
                              
                              OrderStatus(String desc) {
                                  this.desc = desc;
                              }
                          }

                          3. 自定义类型处理器(TypeHandler)

                          3.1 基于字符串的转换

                          @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 基于编码的转换(推荐)

                          @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());
                           PUObwKDs   }
                          
                              @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. 枚举值关联表方案

                          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. 内置枚举处理器

                          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存储结构​​:

                          {
                            "NEW": 10,
                            "PAID": 5,
                            "DELIVERED": 3
                          }

                          三、枚举映射最佳实践方案

                          1. 防御式枚举设计

                          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(
                                                  "无效状态码: " + code));
                                  }
                                  return status;
                              }
                          }

                          2. 枚举基类设计

                          publihttp://www.devze.comc interface androidEnumCode {
                              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);
                              }
                          }

                          四、实战应用场景

                          1. 多状态组合(BIT存储)

                          public enum Permission {
                              READ(1), WRITE(2), DELETE(4), EXECUTE(8);
                              
                              private final int bitValue;
                              
                              // 存储所有权限值到单个整数
                              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. 动态状态机验证

                          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("状态转换非法: " + 
                                             oldStatus + " -> " + newStatus);
                                  }
                                  
                                  order.setStatus(newStatus);
                                  orderMapper.updateById(order);
                              }
                          }
                          
                          // 枚举中定义状态转移规则
                          public enum OrderStatus {
                              NEW {
                                  @Override
                                  public boolean isAllowedTransition(OrderStatus newStatus) {
                                      return newStatus == PAID || newStatus == CANCELLED;
                                  }
                              },
                              // 其他状态定义...
                          }

                          五、性能优化技巧

                          ​​静态映射缓存​​:

                          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));
                              }
                          }

                          ​批量处理优化​​:

                          @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);
                          }

                          ​数据库约束优化​​:

                          -- MySQL ENUM 类型约束
                          status ENUM('NEW','PAID','DELIVERED','CANCELLED') NOT NULL DEFAULT 'NEW'
                          COMMENT '订单状态'
                          
                          -- PostgreSQL检查约束
                          ALTER TABLE orders
                          ADD CONSTRAINT status_check 
                          CHECK (status IN ('NEW', 'PAID', 'DELIVERED', 'CANCELLED'));

                          六、错误解决方案手册

                          1. 序列化问题修复

                          // 添加枚举序列化配置
                          @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=普通用户

                          总结:MyBatis枚举映射决策树

                          graph TD
                              A[需要映射枚举] --> B{是否简单状态值?}
                              B --> |是| C{是否确定永不修改顺序?}
                              C --> |是| D[使用EnumTypeHandler默认序数映射]
                              C --> |否| E[使用EnumOrdinalTypeHandler名称映射]
                              B --> |否| F{是否需要业务编码?}
                              F --> |是| G[自定义TypeHandler+编码设计]
                              F --> |否| H{是否多语言/复杂属性?}
                              H --> |是| I[关联表映射方案]
                              H --> |否| J[直接使用MyBatis-Plus枚举javascript方案]

                          ​企业级应用建议​​:

                          1. 选择​​自定义编码映射​​作为默认方案
                          2. 对性能敏感的常量枚举使用​​序数映射​
                          3. 需要多语言支持的使用​​关联表映射​
                          4. 组合状态使用​​BIT存储+解码方案​

                          遵循这些模式,可构建出健壮且易维护的枚举持久化层,完美平衡数据库高效存储与业务代码的可读性需求。

                          到此这篇关于MyBatis 枚举映射的实现示例的文章就介绍到这了,更多相关MyBatis 枚举映射内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

                          0

                          上一篇:

                          下一篇:

                          精彩评论

                          暂无评论...
                          验证码 换一张
                          取 消

                          最新开发

                          开发排行榜