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

迁移至Hibernate 6:ArrayList数据持久化及旧数据兼容咨询

问题与解决方案

原问题背景

实体类定义:

import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.ArrayList;

@Getter
@Setter
@Entity(name = "trackgeodata")
@Table(name = "trackgeodata")
public class TrackGeodata implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Lob
    @Column(name = "times")
    private ArrayList<ZonedDateTime[]> times;

    @Lob
    @Column(name = "altitudes")
    private ArrayList<int[]> altitudes;  

}

此前通过@Lob注解存储嵌套集合类型数据运行正常,迁移至Spring Boot 3.2 + Hibernate 6时出现错误:

class org.hibernate.type.internal.BasicTypeImpl cannot be cast to class org.hibernate.type.BasicPluralType (org.hibernate.type.internal.BasicTypeImpl and org.hibernate.type.BasicPluralType are in unnamed module of loader 'app')

1. Hibernate 6持久化嵌套ArrayList的正确方式

Hibernate 6对类型映射的校验更严格,不再支持直接用@Lob隐式序列化嵌套集合,推荐两种方案:

方案一:JSON格式存储(推荐)

将嵌套集合序列化为JSON存储,可读性强且支持数据库原生JSON查询,适配主流数据库的JSON类型:

import org.hibernate.type.SqlTypes;
// ... 其他导入

@Getter
@Setter
@Entity(name = "trackgeodata")
@Table(name = "trackgeodata")
public class TrackGeodata implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    // PostgreSQL用jsonb,MySQL用json,根据使用的数据库调整columnDefinition
    @Column(name = "times", columnDefinition = "jsonb")
    @JdbcTypeCode(SqlTypes.JSON)
    private ArrayList<ZonedDateTime[]> times;

    @Column(name = "altitudes", columnDefinition = "jsonb")
    @JdbcTypeCode(SqlTypes.JSON)
    private ArrayList<int[]> altitudes;  

}

Hibernate会自动完成集合与JSON的序列化/反序列化,无需额外处理。

方案二:二进制序列化存储(延续旧逻辑)

如果要坚持用二进制LOB存储,需要显式指定序列化类型:

import org.hibernate.annotations.Type;
import org.hibernate.type.SerializableType;
// ... 其他导入

// ... 类定义省略
@Lob
@Type(SerializableType.class)
@Column(name = "times")
private ArrayList<ZonedDateTime[]> times;

@Lob
@Type(SerializableType.class)
@Column(name = "altitudes")
private ArrayList<int[]> altitudes;

这种方式延续旧的Java序列化逻辑,但二进制数据可读性差,不利于后续查询和维护。


2. 兼容旧数据的迁移方案

旧数据是通过Java序列化存储的二进制LOB,需根据选择的新存储方式处理:

若选择JSON存储方案

  1. 新增JSON字段:修改数据库表,新增两个JSON类型字段(比如times_json、altitudes_json),保留原有的times、altitudes二进制字段。
  2. 批量迁移数据:编写迁移脚本或Spring Boot批量处理代码,遍历所有trackgeodata记录:
    • 读取原二进制字段的数据,反序列化为对应的集合对象。
    • 将集合序列化为JSON字符串,写入新增的JSON字段。
  3. 切换实体映射:修改实体类,将字段映射到新增的JSON字段,删除原@Lob相关配置。
  4. 清理旧字段:确认数据迁移完成且系统稳定运行后,删除原二进制LOB字段。

若选择二进制序列化方案

直接修改实体类添加@Type(SerializableType.class)即可,无需数据迁移,旧数据可直接读取。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:23:14