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

Spring JPA Hibernate 如何支持PostgreSQL JSONB[]类型字段

如何在Hibernate中映射PostgreSQL的JSONB[]类型列

PostgreSQL的JSONB[]是数据库级别的数组类型,每个元素都是独立的JSONB对象——这和JSONB字段内部嵌套的数组完全是两回事。Hypersistence Utils默认只支持单个JSONB/JSON类型或简单数组(比如String[]),没法直接处理这种场景,所以得自定义Hibernate类型来实现JSONB[]和List<Review>的双向转换。

解决方案:自定义Hibernate UserType

直接实现Hibernate的UserType接口,手动处理数据库和实体类之间的序列化/反序列化逻辑:

1. 编写自定义类型类

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.Serializable;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class JsonbArrayUserType implements UserType {

    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final int SQL_TYPE = Types.ARRAY;

    @Override
    public int[] sqlTypes() {
        return new int[]{SQL_TYPE};
    }

    @Override
    public Class<List<Review>> returnedClass() {
        return (Class<List<Review>>) (Class<?>) List.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y) return true;
        if (x == null || y == null) return false;
        return x.equals(y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
        Array array = rs.getArray(names[0]);
        if (array == null) {
            return new ArrayList<>();
        }
        Object[] jsonbObjects = (Object[]) array.getArray();
        List<Review> reviews = new ArrayList<>();
        for (Object jsonbObj : jsonbObjects) {
            try {
                Review review = objectMapper.readValue(jsonbObj.toString(), Review.class);
                reviews.add(review);
            } catch (Exception e) {
                throw new HibernateException("反序列化JSONB对象到Review失败", e);
            }
        }
        return reviews;
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, SQL_TYPE);
            return;
        }
        List<Review> reviews = (List<Review>) value;
        Object[] jsonStrings = new Object[reviews.size()];
        for (int i = 0; i < reviews.size(); i++) {
            try {
                jsonStrings[i] = objectMapper.writeValueAsString(reviews.get(i));
            } catch (Exception e) {
                throw new HibernateException("序列化Review到JSON字符串失败", e);
            }
        }
        Array array = session.getJdbcConnectionAccess().obtainConnection().createArrayOf("jsonb", jsonStrings);
        st.setArray(index, array);
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        if (value == null) return null;
        List<Review> original = (List<Review>) value;
        List<Review> copy = new ArrayList<>();
        for (Review review : original) {
            copy.add(new Review(review.getReviewText(), review.getReviewer()));
        }
        return copy;
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) deepCopy(value);
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return deepCopy(cached);
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }
}

2. 修改实体类,指定自定义类型

在Item类的reviews字段上添加@Type注解,关联刚才写的自定义类型:

@Entity
@Table(name = "items")
public class Item {

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

    @Type(type = "com.yourpackage.JsonbArrayUserType") // 替换成你的实际包路径
    @Column(name = "reviews", columnDefinition = "jsonb[]")
    private List<Review> reviews;

    // 构造函数、getter、setter自行补充
}

注意事项

  • 确保项目中引入了Jackson依赖(用于JSON序列化/反序列化)
  • Review类要正确实现Serializable(你已经完成了这一步)
  • 如果需要支持其他实体类的JSONB[]映射,可以把自定义类型改成泛型版本,适配更多场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 22:57:49