使用Hibernate映射数组

你可以帮我映射一下Hbernate吗?

public class MyClass{ private Long id; private String name; private int[] values; ... } 

我正在使用PostgreSQL,表格的列类型是整数[]我的数组应该如何映射?

我从未将数组映射到hibernate状态。 我总是使用collections品。 所以,我稍微改变了你的课程:

 public class MyClass{ private Long id; private String name; private List values; @Id // this is only if your id is really auto generated @GeneratedValue(strategy=GenerationType.AUTO) public Long getId() { return id; } @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY) public List getValues() { return values; } ... 

Hibernate(和JPA)无法直接映射PostgreSQL数组类型。 如果您确实需要保留数据库结构, 请参阅此问题以了解如何继续。 此线程具有所需自定义类型的示例。

如果你可以改变你的模式,你可以让hibernate创建一个额外的表来处理集合 – List 。 然后,根据你使用的hibernate的版本:

  • 符合JPA 2.0 – 使用@ElementCollection
  • 符合JPA 1.0 – 使用@CollectionOfElements

Hibernate只能映射原始类型。 检查hibernate jar包的org.hibernate.type文件夹下。 int数组不是其中之一。 因此,您必须编写可以实现UserType接口的自定义类型。

 public class MyClass{ private Long id; private String name; private Integer[] values; @Type(type = "com.usertype.IntArrayUserType") public Integer[] getValues(){ return values; } public void setValues(Integer[] values){ this.values = values; } } 

IntArrayUserType.class

 package com.usertype.IntArrayUserType; public class IntArrayUserType implements UserType { protected static final int[] SQL_TYPES = { Types.ARRAY }; @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return this.deepCopy(cached); } @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Integer[]) this.deepCopy(value); } @Override public boolean equals(Object x, Object y) throws HibernateException { if (x == null) { return y == null; } return x.equals(y); } @Override public int hashCode(Object x) throws HibernateException { return x.hashCode(); } @Override public boolean isMutable() { return true; } @Override public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { if (resultSet.wasNull()) { return null; } if(resultSet.getArray(names[0]) == null){ return new Integer[0]; } Array array = resultSet.getArray(names[0]); Integer[] javaArray = (Integer[]) array.getArray(); return javaArray; } @Override public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { Connection connection = statement.getConnection(); if (value == null) { statement.setNull(index, SQL_TYPES[0]); } else { Integer[] castObject = (Integer[]) value; Array array = connection.createArrayOf("integer", castObject); statement.setArray(index, array); } } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } @Override public Class returnedClass() { return Integer[].class; } @Override public int[] sqlTypes() { return new int[] { Types.ARRAY }; } 

当您查询MyClass实体时,您可以添加如下内容:

 Type intArrayType = new TypeLocatorImpl(new TypeResolver()).custom(IntArrayUserType.class); Query query = getSession().createSQLQuery("select values from MyClass") .addScalar("values", intArrayType); List results = (List) query.list(); 

正如我在本文中解释的那样,使用Hibernate映射数组需要一个自定义类型。

所以,假设您像这样定义IntArrayType

 public class IntArrayType extends AbstractSingleColumnStandardBasicType implements DynamicParameterizedType { public IntArrayType() { super( ArraySqlTypeDescriptor.INSTANCE, IntArrayTypeDescriptor.INSTANCE ); } public String getName() { return "int-array"; } @Override protected boolean registerUnderJavaType() { return true; } @Override public void setParameterValues(Properties parameters) { ((IntArrayTypeDescriptor) getJavaTypeDescriptor()) .setParameterValues(parameters); } } 

您还需要ArraySqlTypeDescriptor

 public class ArraySqlTypeDescriptor implements SqlTypeDescriptor { public static final ArraySqlTypeDescriptor INSTANCE = new ArraySqlTypeDescriptor(); @Override public int getSqlType() { return Types.ARRAY; } @Override public boolean canBeRemapped() { return true; } @Override public  ValueBinder getBinder( JavaTypeDescriptor javaTypeDescriptor) { return new BasicBinder( javaTypeDescriptor, this) { @Override protected void doBind( PreparedStatement st, X value, int index, WrapperOptions options ) throws SQLException { AbstractArrayTypeDescriptor abstractArrayTypeDescriptor = (AbstractArrayTypeDescriptor) javaTypeDescriptor; st.setArray( index, st.getConnection().createArrayOf( abstractArrayTypeDescriptor.getSqlArrayType(), abstractArrayTypeDescriptor.unwrap( value, Object[].class, options ) ) ); } @Override protected void doBind( CallableStatement st, X value, String name, WrapperOptions options ) throws SQLException { throw new UnsupportedOperationException( "Binding by name is not supported!" ); } }; } @Override public  ValueExtractor getExtractor( final JavaTypeDescriptor javaTypeDescriptor) { return new BasicExtractor(javaTypeDescriptor, this) { @Override protected X doExtract( ResultSet rs, String name, WrapperOptions options ) throws SQLException { return javaTypeDescriptor.wrap( rs.getArray(name), options ); } @Override protected X doExtract( CallableStatement statement, int index, WrapperOptions options ) throws SQLException { return javaTypeDescriptor.wrap( statement.getArray(index), options ); } @Override protected X doExtract( CallableStatement statement, String name, WrapperOptions options ) throws SQLException { return javaTypeDescriptor.wrap( statement.getArray(name), options ); } }; } } 

IntArrayTypeDescriptor

 public class IntArrayTypeDescriptor extends AbstractArrayTypeDescriptor { public static final IntArrayTypeDescriptor INSTANCE = new IntArrayTypeDescriptor(); public IntArrayTypeDescriptor() { super( int[].class ); } @Override protected String getSqlArrayType() { return "integer"; } } 

Java-to-JDBC类型处理的大部分包含在AbstractArrayTypeDescriptor基类中:

 public abstract class AbstractArrayTypeDescriptor extends AbstractTypeDescriptor implements DynamicParameterizedType { private Class arrayObjectClass; @Override public void setParameterValues(Properties parameters) { arrayObjectClass = ( (ParameterType) parameters .get( PARAMETER_TYPE ) ) .getReturnedClass(); } public AbstractArrayTypeDescriptor(Class arrayObjectClass) { super( arrayObjectClass, (MutabilityPlan) new MutableMutabilityPlan() { @Override protected T deepCopyNotNull(Object value) { return ArrayUtil.deepCopy( value ); } } ); this.arrayObjectClass = arrayObjectClass; } @Override public boolean areEqual(Object one, Object another) { if ( one == another ) { return true; } if ( one == null || another == null ) { return false; } return ArrayUtil.isEquals( one, another ); } @Override public String toString(Object value) { return Arrays.deepToString((Object[]) value); } @Override public T fromString(String string) { return ArrayUtil.fromString( string, arrayObjectClass ); } @SuppressWarnings({ "unchecked" }) @Override public  X unwrap( T value, Class type, WrapperOptions options ) { return (X) ArrayUtil.wrapArray( value ); } @Override public  T wrap( X value, WrapperOptions options ) { if( value instanceof Array ) { Array array = (Array) value; try { return ArrayUtil.unwrapArray( (Object[]) array.getArray(), arrayObjectClass ); } catch (SQLException e) { throw new IllegalArgumentException( e ); } } return (T) value; } protected abstract String getSqlArrayType(); } 

AbstractArrayTypeDescriptor依赖于ArrayUtil来处理Java数组深度复制,包装和解包逻辑。

现在,您的映射将如下所示:

 @Entity(name = "Event") @Table(name = "event") @TypeDef( name = "int-array", typeClass = IntArrayType.class ) public static class Event extends BaseEntity { @Type( type = "int-array" ) @Column( name = "sensor_values", columnDefinition = "integer[]" ) private int[] sensorValues; //Getters and setters omitted for brevity }