Spring Boot中JPA/Hibernate存JSON报错:原生SQL正常却提示字段过长
我正在开发基于Spring Boot的应用,使用JPA和Hibernate框架,尝试通过自定义@Converter将字段序列化为JSON后,持久化到MySQL的varchar(2000)列中。
DTO
@XmlElement private Object value;
实体类
@Column(name = "value") @Convert(converter = JpaConverterJson.class) private Object value;
转换器
@Component @Converter @Slf4j public class JpaConverterJson implements AttributeConverter<Object, String> { @Autowired private ObjectMapper objectMapper; @Override public String convertToDatabaseColumn(Object meta) { try { objectMapper.disable(SerializationFeature.WRAP_ROOT_VALUE); return objectMapper.writeValueAsString(meta); } catch (JsonProcessingException ex) { log.error("JsonProcessingException encoding Object into String: {}", ex.getMessage()); return null; } } @Override public Object convertToEntityAttribute(String dbData) { try { return objectMapper.readValue(dbData, Object.class); } catch (IOException ex) { log.error("IOException decoding json from database: {}", ex.getMessage()); return null; } } }
问题
执行原生SQL INSERT语句时,数据可正常存储;但通过JPA持久化实体时,出现如下错误:
com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Data too long for column 'value' at row 1
为何原生SQL能正常存储,而Hibernate却无法持久化数据?
原因分析
Hibernate预校验逻辑限制
实体类中value字段为Object类型,且@Column未显式指定length属性,Hibernate会默认将其映射为长度255的字符串类型。在持久化时,Hibernate会先校验数据长度是否符合这个默认限制,即便转换器序列化后的JSON字符串长度在MySQL列的2000限制内,也会因Hibernate的预校验触发错误。而原生SQL直接操作数据库,不会经过这层校验,只要数据长度不超过列定义就能存储。MySQL模式差异
若MySQL未开启严格模式(如STRICT_TRANS_TABLES),原生SQL插入过长数据时会自动截断并完成插入;但Hibernate默认严格遵循校验规则,直接抛出异常,不会自动截断数据。
解决方法
显式指定字段长度
修改实体类的@Column注解,将length设置为与数据库列一致的2000:@Column(name = "value", length = 2000) @Convert(converter = JpaConverterJson.class) private Object value;这样Hibernate会按照指定长度校验数据,只要序列化后的JSON字符串长度不超过2000,就能正常持久化。
开启MySQL严格模式(可选)
若想避免MySQL自动截断数据的隐式行为,可开启STRICT_TRANS_TABLES模式,这样原生SQL插入过长数据时也会报错,便于提前发现数据长度问题。
内容的提问来源于stack exchange,提问作者Ibrahim Alvi

