关于 enum 枚举的全栈应用:数据库(sql server)、后端(Spring Boot+MyBatis-Plus)、前端(Vue3+TypeScript)
数据库(sql server)
CREATE TABLE [dbo].[Activity](
[act_ID] [int] IDENTITY(1,1) NOT NULL,
[act_ConcretWorkflowID] [int] NOT NULL,
[act_UserDefinedNo] [varchar](20) NOT NULL,
[act_ActorName] [varchar](40) NOT NULL,
[act_Name] [varchar](100) NOT NULL,
[act_IsStartNode] [bit] NOT NULL,
[act_DefaultActor] [varchar](15) NOT NULL,
[act_Remark] [varchar](200) NULL,
[act_ExecuteType] [int] NOT NULL,
[act_FilterType] [int] NOT NULL,
[act_WithdrawType] [int] NULL,
[act_ExecuteAction] [varchar](50) NULL,
[act_WithdrawAction] [varchar](50) NULL,
[act_Idea] [varchar](500) NULL,
[act_FilterValue] [varchar](500) NULL,
[act_Classified] [varchar](100) NULL,
[act_File] [image] NULL,
[act_FileName] [varchar](255) NULL,
CONSTRAINT [PK_Activity] PRIMARY KEY CLUSTERED
(
[act_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
提交方式:数据表字段名是 act_ExecuteType,数据类型是 int
后端(Spring Boot+MyBatis-Plus)
枚举的定义
src/main/java/com/weiyu/enumeration/ActivitySubmitMode.java
package com.weiyu.enumeration;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
/**
* 活动提交方式枚举
*/
@Getter
public enum ActivitySubmitMode {
/**
* 提交给指定人员
*/
PERSONNEL(0, "提交给指定人员"),
/**
* 提交给角色或人
*/
ROLE(1, "提交给角色或人");
@EnumValue // MyBatis-Plus 数据库映射,标记使用该字段的值存入数据库,保存到数据库就是0、1、2等整数
@JsonValue // Jackson JSON 序列化,标记使用该字段的值进行序列化,前端接收到的就是0、1、2等数字
private final Integer value;
//@JsonValue // Jackson JSON 序列化,标记使用该字段的值进行序列化,前端接收到的就是提交给指定人员、提交给角色或人等字符串
private final String displayName;
ActivitySubmitMode(Integer value, String displayName) {
this.value = value;
this.displayName = displayName;
}
}
实体类的定义
src/main/java/com/weiyu/model/Activity.java
package com.weiyu.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.weiyu.enumeration.ActivityRejectMode;
import com.weiyu.enumeration.ActivitySubmitMode;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* 活动实体类
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@TableName("Activity")
public class Activity {
/**
* 主键id
*/
@TableId(value = "act_ID", type = IdType.AUTO)
private Integer id;
/**
* 流程版本id
*/
@TableField("act_ConcretWorkflowID")
@NotNull
@Min(1)
private Integer concretWorkflowId;
/**
* 活动名称
*/
@TableField("act_Name")
@NotBlank
@Size(max = 100)
private String name;
/**
* 是否首活动(启动节点)
*/
@TableField("act_IsStartNode")
@NotNull
private Boolean isStartNode;
/**
* 办理意见
*/
@TableField("act_Idea")
@Size(max = 500)
private String idea;
/**
* 过滤模式
*/
@TableField("act_FilterType")
@NotNull
@Min(0)
private Integer filterType;
/**
* 过滤内容
*/
@TableField("act_FilterValue")
@Size(max = 500)
private String filterValue;
/**
* 提交方式
*/
@TableField("act_ExecuteType")
private ActivitySubmitMode submitMode;
/**
* 退回方式
*/
@TableField("act_WithdrawType")
private ActivityRejectMode rejectMode;
}
VO的定义
src/main/java/com/weiyu/model/AuditOptionVO.java
package com.weiyu.model;
import com.weiyu.enumeration.ActivityRejectMode;
import com.weiyu.enumeration.ActivitySubmitMode;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 办理选项 VO
*/
@Data
@NoArgsConstructor
public class AuditOptionVO {
/**
* 是否最后办理
*/
private Boolean isLastAudit;
/**
* 办理意见选项
*/
private List<String> auditIdeaOptions;
/**
* 目标办理人(角色成员)选项
*/
private List<RoleMemberDTO> auditMemberOptions;
/**
* 目标活动名称
*/
private String targetActivityName;
/**
* 提交方式
*/
private ActivitySubmitMode submitMode;
/**
* 退回方式
*/
private ActivityRejectMode rejectMode;
}
前端(Vue3+TypeScript)
枚举的定义
src\types\Workflow.enum.ts
/**
* 活动提交方式枚举
*/
export enum ActivitySubmitType {
/**
* 提交给指定人员
*/
PERSONNEL = 0,
/**
* 提交给角色或人
*/
ROLE = 1
}
响应接口的定义
src\types\Workflow.ts
/**
* 办理选项
*/
export interface AuditOption {
/** 是否最后办理 */
isLastAudit: boolean;
/** 办理意见选项 */
auditIdeaOptions: string[];
/** 目标办理人(角色成员)选项 */
auditMemberOptions: RoleMember[];
/** 目标活动名称 */
targetActivityName: string;
/** 提交方式 */
submitMode: ActivitySubmitType;
/** 退回方式 */
rejectMode: ActivityRejectMode;
}
分析讲解
在 Spring Boot 中使用 MyBatis-Plus 时,实体类直接使用枚举类型是可行的,但需要根据你期望的数据库存储值进行额外配置。你当前的代码存在一些潜在问题,下面详细分析并给出建议。
一、当前代码存在的问题
1. 枚举字段的默认映射规则
MyBatis-Plus(基于 MyBatis)默认使用 EnumTypeHandler 处理枚举类型,它会:
-
存储枚举的
name()(字符串) 或者ordinal()(序号),具体取决于全局配置(默认可能是EnumOrdinalTypeHandler或EnumTypeHandler,取决于版本和配置)。 -
不会自动使用枚举中自定义的
value字段(如你定义的0和1)。
如果你的数据库对应字段是整型(比如 submit_mode 列类型为 INT),而 MyBatis-Plus 存储了枚举名称(字符串),就会导致类型不匹配异常;如果存储了序号,虽然类型匹配(整数),但序号可能与你期望的 value 不一致,且枚举顺序变化会导致历史数据错乱。
2. 缺少必要的注解
你使用了 @TableField 注解指定了数据库列名,但没有告诉 MyBatis-Plus 如何将枚举转换为数据库值。因此 MyBatis-Plus 会使用默认处理器,可能不是你想要的整数值。
3. 枚举中的 value 字段未与 MyBatis-Plus 关联
你的枚举类定义了 value 字段并提供了构造方法,但没有通过任何注解标记它作为数据库映射的值,所以 MyBatis-Plus 完全不知道这个字段的存在。
二、如何正确实现:存储枚举的整数值
方案一:使用 @EnumValue 注解(推荐)
MyBatis-Plus 提供了 @EnumValue 注解,可以标记在枚举的字段上,指定该字段的值作为数据库存储值。
修改你的枚举类:
java
package com.weiyu.enumeration;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum ActivitySubmitMode {
PERSONNEL(0),
ROLE(1);
@EnumValue // 标记使用该字段的值存入数据库
private final Integer value;
ActivitySubmitMode(Integer value) {
this.value = value;
}
// 可以添加 getter 方法,方便业务中使用
public Integer getValue() {
return value;
}
}
同样修改 ActivityRejectMode 枚举。
这样配置后:
-
当 MyBatis-Plus 保存
Activity对象时,会将submitMode枚举的value字段值(0 或 1)写入数据库。 -
查询时,也会根据数据库的整数值自动转换为对应的枚举实例(需要枚举中定义对应的值,且值唯一)。
方案二:实现 IEnum 接口
MyBatis-Plus 还提供了 IEnum 接口,枚举实现该接口并重写 getValue() 方法即可。
java
public enum ActivitySubmitMode implements IEnum<Integer> {
PERSONNEL(0),
ROLE(1);
private final Integer value;
ActivitySubmitMode(Integer value) {
this.value = value;
}
@Override
public Integer getValue() {
return value;
}
}
效果与 @EnumValue 类似,但 IEnum 接口可能更适用于需要序列化等场景。
方案三:自定义类型处理器
如果需求复杂(例如需要额外的转换逻辑),可以编写自定义的 TypeHandler,但通常前两种方案已经足够。
三、实体类的其他注意事项
1. 数据库列类型
确保数据库表字段 submit_mode 和 reject_mode 的类型与存储值匹配(如 INT 或 TINYINT)。如果使用 @EnumValue 标记的字段是 Integer,数据库应为整数类型。
2. MyBatis-Plus 版本
@EnumValue 注解在 MyBatis-Plus 的较新版本中均支持,建议使用最新稳定版。
3. 布尔类型字段
你的 isStartNode 字段是 Boolean 类型,MyBatis-Plus 会自动处理,通常数据库对应 TINYINT(1) 或 BIT,无需额外配置。
4. 验证注解
@NotNull、@Min 等验证注解在实体类中定义是合理的,会在 Controller 层触发校验(需配合 @Valid 使用)。
四、总结
当前代码不可直接使用,因为 MyBatis-Plus 默认无法将枚举转换为自定义的整数值。你需要:
-
在枚举的
value字段上添加@EnumValue注解(或实现IEnum接口)。 -
确保数据库字段类型与
value类型一致。 -
确认 MyBatis-Plus 配置正确(通常无需额外配置,注解即生效)。
修改后的枚举示例:
java
public enum ActivitySubmitMode {
PERSONNEL(0),
ROLE(1);
@EnumValue
private final Integer value;
ActivitySubmitMode(Integer value) {
this.value = value;
}
}
实体类保持不变即可。这样,你的设计就是可行且优雅的:既保持了代码的可读性(使用枚举),又实现了数据库存储整数值的灵活性。
枚举中可以使用@Getter吗?
在 Java 枚举中使用 Lombok 的 @Getter 注解是完全可行的,它能够为枚举的字段自动生成 getter 方法,简化代码。
一、基本用法
在枚举上使用 @Getter 与在普通类中类似。你可以:
-
在枚举类上添加
@Getter,为所有字段生成 getter。 -
在特定字段上添加
@Getter,仅为该字段生成 getter。
示例代码
java
import lombok.Getter;
@Getter
public enum ActivitySubmitMode {
PERSONNEL(0),
ROLE(1);
private final Integer value;
ActivitySubmitMode(Integer value) {
this.value = value;
}
}
编译后,Lombok 会自动生成 getValue() 方法,你可以在业务代码中通过 ActivitySubmitMode.PERSONNEL.getValue() 获取枚举对应的整数值。
二、注意事项
1. 枚举字段的不可变性
枚举的字段通常是 final 的(推荐如此),Lombok 生成的 getter 方法只是读取字段值,不会尝试修改,因此完全兼容。
2. 构造器默认私有
枚举的构造器默认为 private,Lombok 不会干涉这一点,你仍然需要自己编写构造器(或使用 @AllArgsConstructor 简化,但要注意枚举构造器必须是私有的,Lombok 的 @AllArgsConstructor 默认生成 public 构造器,需手动调整,因此更建议手动编写构造器)。
3. 使用 @Data 需谨慎
@Data 包含 @ToString、@EqualsAndHashCode、@Getter、@Setter 等。在枚举上使用 @Data 可能会生成 setter 方法(但枚举字段不可变,setter 没有意义),且 toString、equals、hashCode 可能覆盖了枚举默认的实现(通常不需要)。因此,一般只使用 @Getter 即可。
4. 结合 MyBatis-Plus 的 @EnumValue
如果你使用 MyBatis-Plus 的 @EnumValue 标记数据库存储字段,那么为该字段生成 getter 方法(通过 @Getter)可以方便地在业务层获取其值,但并非强制要求。@EnumValue 本身不依赖 getter。
示例:
java
public enum ActivitySubmitMode {
PERSONNEL(0),
ROLE(1);
@EnumValue
@Getter
private final Integer value;
ActivitySubmitMode(Integer value) {
this.value = value;
}
}
三、为什么要在枚举中使用 @Getter?
-
代码简洁:避免手动编写样板 getter 方法。
-
方便取值:在需要获取枚举关联值(如数据库存储的整型代码)时,直接通过 getter 访问,提高可读性。
-
与框架集成:某些框架可能通过 getter 访问属性(如 JSON 序列化),
@Getter可以确保符合 JavaBean 规范。
四、总结
可以,而且推荐在枚举中使用 @Getter,它能帮助保持代码简洁,同时不会影响枚举自身的特性。只需注意在枚举上避免使用可能带来副作用的 Lombok 注解(如 @Data、@Setter),并手动处理构造器即可。
为什么前端vue3接收到的是:submitMode: "ROLE"
这是因为 Spring Boot 默认使用 Jackson 进行 JSON 序列化,而 Jackson 在处理枚举类型时,默认将枚举序列化为其名称(name()),即字符串 "ROLE"。这与 MyBatis-Plus 的数据库映射(你使用了 @EnumValue 存储整数值)是两套独立的机制,互不影响。
为什么会出现这种现象?
-
MyBatis-Plus 的
@EnumValue:只控制实体类与数据库之间的转换(写入/读取)。当从数据库查询数据时,MyBatis-Plus 会根据整数值自动组装成对应的枚举实例(例如数据库存1会映射为ActivitySubmitMode.ROLE)。 -
Jackson 序列化:当 Controller 返回
Activity对象时,Spring 会使用 Jackson 将对象转换为 JSON。Jackson 默认将枚举输出为Enum.name(),因此你看到submitMode: "ROLE"。
如何让前端收到你期望的值?
场景一:前端需要整数值(如 0 或 1)
你希望前端看到 submitMode: 1,可以使用 Jackson 的 @JsonValue 注解,指定序列化时使用枚举的哪个字段。
修改枚举类:
java
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
@Getter
public enum ActivitySubmitMode {
PERSONNEL(0),
ROLE(1);
@EnumValue // MyBatis-Plus 数据库映射
@JsonValue // Jackson JSON 序列化
private final Integer value;
ActivitySubmitMode(Integer value) {
this.value = value;
}
}
加上 @JsonValue 后,Jackson 会使用 value 字段的值进行序列化,前端收到的 JSON 将是 submitMode: 1(整数)。
场景二:前端需要枚举名称(当前现象)
如果你确实需要前端收到 "ROLE",那么当前行为就是正确的,无需修改。
场景三:前端需要自定义字符串(如 "角色")
如果希望返回更友好的显示文本,可以在枚举中添加额外字段,并用 @JsonValue 标记该字段。
java
@Getter
public enum ActivitySubmitMode {
PERSONNEL(0, "指定人员"),
ROLE(1, "角色或人");
@EnumValue
private final Integer value;
@JsonValue
private final String displayName;
ActivitySubmitMode(Integer value, String displayName) {
this.value = value;
this.displayName = displayName;
}
}
此时前端收到 submitMode: "角色或人"。
注意事项
-
@JsonValue会影响整个枚举的序列化,包括在 Map 中作为 key 等情况,确保你了解其作用范围。 -
如果同一个枚举需要不同的序列化方式(例如在某些接口输出名称,在某些接口输出代码),建议使用 DTO(数据传输对象),在 DTO 中定义需要的字段类型,并在业务层手动转换。
-
如果全局都需要将枚举输出为代码,也可以配置 Jackson 的
SerializationFeature.WRITE_ENUMS_USING_TO_STRING或自定义序列化器,但@JsonValue是最直接的方式。
总结
-
当前看到
"ROLE"是 Jackson 的默认行为。 -
若希望前端收到整数值,只需在枚举的
value字段上添加@JsonValue注解。 -
@EnumValue和@JsonValue可以共存,分别服务于数据库映射和 JSON 序列化。
这样,你的实体类就能同时满足数据库存储整数、前端接收整数的需求了。
更多推荐




所有评论(0)