You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3.1.4+Hibernate6.2.9升级后JSON存储字符集截断错误

问题背景

近期将应用升级至Spring Boot 3.1.4、Java 21及Hibernate 6.2.9版本后,向MySQL数据库存储JSON数据时遇到以下错误:

WARN 1 --- [erContainer#0-2] o.h.engine.jdbc.spi.SqlExceptionHelper
: SQL Error: 3144, SQLState: 22001
Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.

相关代码

@Service
public class JpaDataWritingServiceImpl implements DataWritingService {
    @Autowired
    TestDataRepository testDataRepository;
    @Autowired
    TestRepository testRepository;

    @Override
    public void writetestData(Long testId, String type, byte[] data, testStatus testStatus, boolean needsBackgroundtest) {
        Optional<Test> maybeTest = testRepository.findById(testId);
        if (maybeTest.isPresent()) {
            Test test = maybeTest.get();
            String metadata = generateMetadataForTestData(data);

            TestData testData = new TestData(test, type, data, metadata);

            LOG.debug("Saving TestData======> data={}, metadata={}", dataString, metadata); // 保存前验证数据

            testDataRepository.save(new TestData(test, type, data, metadata)); // 此处报错
        } else {
            LOG.warn("Didn't find testData with id {}", testId);
        }
    }

    private String generateMetadataForTestData(byte[] data) {
        String byteToStringData = new String(data, StandardCharsets.UTF_8);
        JSONObject byteToString = new JSONObject(byteToStringData);
        JSONObject stringToJson = new JSONObject();

        if (byteToString.has("platform")) {
            stringToJson.put("content_id", byteToString.getString("id"));
            try {
                Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(byteToString.getString("timestamp"));
                long epochTime = date.getTime() / 1000;
                stringToJson.put("timestamp", epochTime);
            } catch (ParseException e) {
                LOG.error("DateTime parse exception: {}", e.getMessage());
            }
        }
        return stringToJson.length() == 0 ? null : stringToJson.toString();
    }
}

错误堆栈跟踪

Caused by: org.springframework.dao.DataIntegrityViolationException: could not execute statement [Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.]
[insert into test_data (test_id, data, metadata, received_at, type) values (?,?,?,?,?)]

实体定义

@Entity
@Table(name="test_data")
public class TestData {

    public TestData(){}

    public TestData(Test test,
                        String type,
                        byte[] data,
                        String metadata) {
        this.test = test;
        this.type = type;
        this.data = data;
        this.metadata = metadata;
        this.receivedAt = Instant.now();
    }

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @ManyToOne private Test test;

    private String type;

    @NotNull @Lob
    private byte[] data;

    @JsonIgnore
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "testData")
    private TestDataTestStatus testStatus;

    @Column(name = "received_at")
    private Instant receivedAt;

    @Lob
    @Column(columnDefinition = "json")
    private String metadata;

    // 其他字段及getter/setter...
}

数据库配置

CREATE TABLE `test_data` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `test_id` bigint NOT NULL,
  `data` mediumblob NOT NULL,
  `received_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `metadata` json DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_test_data_test` (`test_id`),
  CONSTRAINT `fk_test_data_test` FOREIGN KEY (`test_id`) REFERENCES `test` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB AUTO_INCREMENT=175613200 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_ci

观察结果

  • 升级前代码运行正常
  • data字段是序列化后的JSON字节数组,我将其转为字符串后存入metadata列
  • 应用日志显示data和metadata都是合法的JSON字符串:
Saving testData======> data={"id":"113662220040053124", ...}, metadata={"content_id":"113662220040053124","timestamp":1734347839}

问题

  1. 为何现在存储JSON metadata或byte[] data时会出现CHARACTER SET 'binary'错误?
  2. Hibernate 6.2.x或Spring Boot 3.1.x在处理JSON或byte[]字段时有新要求吗?

问题解答

1. 错误原因

问题出在metadata字段的映射逻辑:你给该字段加了@Lob注解,在Hibernate 6.2.x中,@Lob标记的字符串会被默认当作二进制大对象(BLOB)处理,JDBC驱动会以binary字符集将字符串发送给MySQL。但MySQL的JSON列要求传入的字符串必须是UTF-8字符集,无法识别binary类型的输入,因此触发了数据截断错误。

升级前的Hibernate版本对@Lob+columnDefinition="json"的处理逻辑不同,可能会忽略@Lob的优先级,直接按JSON类型处理字符串;但Hibernate 6.x调整了类型映射的优先级,@Lob的权重更高,导致字段被当作BLOB处理,最终引发字符集不匹配问题。

2. Hibernate 6.2.x/Spring Boot 3.1.x的新要求

Hibernate 6.x对类型映射做了更严格的规范:

  • @Lob注解会明确将字段映射为BLOB/CLOB类型,不再与自定义columnDefinition的类型自动兼容
  • 对于JSON类型字段,Hibernate 6.x推荐使用专门的JSON映射方式,而非@Lob+columnDefinition的组合

解决方法

直接移除metadata字段上的@Lob注解即可:

@Column(columnDefinition = "json")
private String metadata;

如果需要兼容更复杂的JSON场景,也可以使用Hibernate的@JdbcTypeCode注解明确指定JSON类型:

@Column(columnDefinition = "json")
@JdbcTypeCode(SqlTypes.JSON)
private String metadata;

另外说明:byte[] data字段的@Lob注解是合理的(对应数据库的mediumblob),该字段与本次错误无关,错误仅来自metadata的类型映射问题。


内容的提问来源于stack exchange,提问作者Rohan Razdan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 11:50:21