MySQL与Java:带描述的枚举存储及JdbcTemplate异常处理求助
我之前做项目的时候也碰到过一模一样的问题,给你分享下我的解决方案:
问题1:数据库应该存储什么值?
强烈建议你存储ACTIVE/AWAITING这种枚举的短标识,而不是Active Order/Awaiting Order这种显示文本,原因有这几点:
- 避免数据截断:你遇到的异常大概率是因为数据库字段长度不够(比如设了
VARCHAR(10)),而Active Order这类文本长度超过了限制。短标识长度固定且短,完全不会有这个问题。 - 数据稳定性:显示文本以后可能需要修改(比如改成"Active Order Item"),但枚举的业务标识(
ACTIVE)不会变,存标识的话不需要修改数据库里的历史数据。 - 语义明确:
ACTIVE这类标识对开发者更友好,看数据库数据就能直接知道状态含义,不需要额外映射。
如果需要给用户显示友好名称,只需要在枚举里定义对应的显示属性,查询后再转换就行(比如给枚举加个displayName字段,调用getDisplayName()获取)。
问题2:JdbcTemplate结合MapSqlParameterSource保存枚举值
分两种常见场景来处理:
场景1:直接存储枚举的name()(推荐)
假设你的枚举定义是这样的:
public enum OrderStatus { ACTIVE("Active Order"), AWAITING("Awaiting Order"); private final String displayName; OrderStatus(String displayName) { this.displayName = displayName; } public String getDisplayName() { return displayName; } }
保存时,直接把枚举的name()作为参数传入MapSqlParameterSource:
String insertSql = "INSERT INTO orders (id, status) VALUES (:orderId, :orderStatus)"; MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("orderId", 123L); // 传入枚举的name(),对应数据库里的ACTIVE/AWAITING params.addValue("orderStatus", orderStatus.name()); jdbcTemplate.update(insertSql, params);
查询时,把数据库返回的字符串转回枚举:
String querySql = "SELECT id, status FROM orders WHERE id = :orderId"; Order result = jdbcTemplate.queryForObject(querySql, new MapSqlParameterSource("orderId", 123L), (rs, rowNum) -> { Order order = new Order(); order.setId(rs.getLong("id")); // 用valueOf把字符串转成枚举 order.setStatus(OrderStatus.valueOf(rs.getString("status"))); return order; });
场景2:自定义数据库存储值(比如枚举有专属的db字段)
如果你的枚举有自定义的数据库存储值(比如不是用name(),而是单独定义的dbValue):
public enum OrderStatus { ACTIVE("active", "Active Order"), AWAITING("awaiting", "Awaiting Order"); private final String dbValue; private final String displayName; OrderStatus(String dbValue, String displayName) { this.dbValue = dbValue; this.displayName = displayName; } public String getDbValue() { return dbValue; } // 提供从dbValue转回枚举的方法 public static OrderStatus fromDbValue(String dbValue) { for (OrderStatus status : values()) { if (status.dbValue.equals(dbValue)) { return status; } } throw new IllegalArgumentException("Unknown status: " + dbValue); } }
保存时传入自定义的dbValue:
params.addValue("orderStatus", orderStatus.getDbValue());
查询时用fromDbValue()转换:
order.setStatus(OrderStatus.fromDbValue(rs.getString("status")));
更优雅的方式:自定义类型转换器
如果不想每次都手动调用name()或getDbValue(),可以自定义一个类型转换器,注册到Spring的类型转换体系里:
import org.springframework.core.convert.converter.Converter; public class OrderStatusToDbValueConverter implements Converter<OrderStatus, String> { @Override public String convert(OrderStatus source) { return source.getDbValue(); // 或者source.name() } }
然后在配置类里注册这个转换器:
import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new OrderStatusToDbValueConverter()); } }
之后就可以直接把枚举对象传入MapSqlParameterSource,Spring会自动帮你转换:
params.addValue("orderStatus", orderStatus);
这样就彻底解决了枚举的持久化问题,也不会再碰到数据截断的异常啦!
内容的提问来源于stack exchange,提问作者Kathiresa J
相关产品推荐
相关产品推荐

