PostgreSQL+Hibernate6中ListArrayType映射自定义数组报错求助
问题描述
在PostgreSQL+Hibernate6环境下,原本将Author实体中的单个Book对象映射为JSONB字段,配置如下:
原实体类与SQL脚本
@Entity public class Author { // ... 其他字段 @Type(JsonBinaryType.class) @Column(name = "book") private Book book; } public class Book { private UUID id; private String name; // ... getter/setter等方法 }
CREATE TABLE author ( -- ... 其他字段 book JSONB, -- ... );
现在需要将单个Book字段替换为Book对象列表,尝试了以下配置:
修改后的配置
@Entity public class Author { // ... 其他字段 @Type( value = ListArrayType.class, parameters = {@Parameter(name = ListArrayType.SQL_ARRAY_TYPE, value = "book")} ) @Column(name = "books", columnDefinition = "book[]") private List<Book> books; }
CREATE TYPE book AS (id UUID, name VARCHAR); CREATE TABLE author ( -- ... 其他字段 books book[], -- ... );
执行后出现异常:
Unable to find server array type for provided name book
请问是否遗漏了配置,还是此方案本身不可行?
解决方案
错误原因
你使用的ListArrayType是用于处理PostgreSQL原生基础类型数组(如int[]、varchar[])的类型,但这里定义的book是自定义复合类型,Hibernate无法自动识别该复合类型对应的数组类型,因此抛出找不到服务器数组类型的异常。
推荐方案:延续JSONB数组(简单高效)
直接将List<Book>映射为JSONB类型,无需创建自定义复合类型,配置逻辑和单个Book的映射一致,只是字段类型从Book改为List<Book>:
实体类配置
@Entity public class Author { // ... 其他字段 @Type(JsonBinaryType.class) @Column(name = "books", columnDefinition = "JSONB") private List<Book> books; }
SQL脚本
CREATE TABLE author ( -- ... 其他字段 books JSONB, -- ... );
这种方式的优势:
- 无需额外创建自定义数据库类型,配置简单
- Hibernate的
JsonBinaryType会自动将List<Book>序列化为JSONB数组,查询时反序列化为对象列表 - 支持PostgreSQL的JSON函数进行数组元素的查询、筛选(如
jsonb_array_elements)
可选方案:使用复合类型数组(复杂度高)
如果一定要使用PostgreSQL复合类型数组,需要自定义Hibernate类型来处理复合类型与对象的转换:
- 确保数据库自定义类型创建成功
CREATE TYPE book AS (id UUID, name VARCHAR);
- 创建复合类型映射类
实现CompositeUserType接口,处理book复合类型与Book对象的转换:
public class BookCompositeType implements CompositeUserType { @Override public String[] getPropertyNames() { return new String[]{"id", "name"}; } @Override public Type[] getPropertyTypes() { return new Type[]{UUIDType.INSTANCE, StringType.INSTANCE}; } @Override public Object getPropertyValue(Object component, int property) throws HibernateException { Book book = (Book) component; return switch (property) { case 0 -> book.getId(); case 1 -> book.getName(); default -> throw new IllegalArgumentException("无效属性索引"); }; } @Override public void setPropertyValue(Object component, int property, Object value) throws HibernateException { Book book = (Book) component; switch (property) { case 0 -> book.setId((UUID) value); case 1 -> book.setName((String) value); default -> throw new IllegalArgumentException("无效属性索引"); } } @Override public Class<?> returnedClass() { return Book.class; } @Override public boolean equals(Object x, Object y) throws HibernateException { if (x == y) return true; if (x == null || y == null) return false; Book b1 = (Book) x; Book b2 = (Book) y; return Objects.equals(b1.getId(), b2.getId()) && Objects.equals(b1.getName(), b2.getName()); } @Override public int hashCode(Object x) throws HibernateException { Book book = (Book) x; return Objects.hash(book.getId(), book.getName()); } @Override public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { UUID id = rs.getUUID(names[0]); String name = rs.getString(names[1]); if (rs.wasNull()) return null; Book book = new Book(); book.setId(id); book.setName(name); return book; } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { if (value == null) { st.setNull(index, Types.OTHER); return; } Book book = (Book) value; st.setObject(index, Arrays.asList(book.getId(), book.getName()), Types.OTHER); } // 实现其他必要方法(deepCopy、disassemble、assemble、replace等) }
- 创建数组类型映射类
继承GenericArrayType(来自Hibernate Types库),指定复合类型名称:
public class BookArrayType extends GenericArrayType<Book> { public BookArrayType() { super(Book.class, "book"); } }
- 实体类中使用自定义类型
@Entity public class Author { // ... 其他字段 @Type(BookArrayType.class) @Column(name = "books", columnDefinition = "book[]") private List<Book> books; }
注意:这种方案需要编写大量自定义类型代码,维护成本高,仅在必须使用复合类型数组的场景下考虑。
内容的提问来源于stack exchange,提问作者puka
相关产品推荐
相关产品推荐

