使用Hibernate映射数组

时间:2010-12-02 06:57:53

标签: java hibernate postgresql java-ee

你能帮我用Hibernate映射这个类吗?

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

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

4 个答案:

答案 0 :(得分:14)

Hibernate(和JPA)无法直接映射PostgreSQL数组类型。 See this question如果您确实需要保留数据库结构,请继续操作。 This thread有一个必需的自定义类型的示例。

如果您可以更改架构,可以让hibernate创建一个额外的表来处理集合 - List<Integer>。然后,根据你正在使用的hibernate的版本:

答案 1 :(得分:6)

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

public class MyClass{
    private Long id;
    private String name;
    private List<Integer> 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<Integer> getValues() {
        return values;
    }   
    ...

答案 2 :(得分:5)

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<Integer[]> 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<Integer[]> results = (List<Integer[]>) query.list();

答案 3 :(得分:0)

正如我在 this article 中所解释的那样,使用Hibernate映射数组需要自定义类型。

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

public class IntArrayType
        extends AbstractSingleColumnStandardBasicType<int[]>
        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 <X> ValueBinder<X> getBinder(
        JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicBinder<X>( javaTypeDescriptor, this) {
            @Override
            protected void doBind(
                    PreparedStatement st, 
                    X value, 
                    int index, 
                    WrapperOptions options
                ) throws SQLException {

                AbstractArrayTypeDescriptor<Object> abstractArrayTypeDescriptor = 
                    (AbstractArrayTypeDescriptor<Object>) 
                        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 <X> ValueExtractor<X> getExtractor(
        final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicExtractor<X>(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<int[]> {

    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<T>
        extends AbstractTypeDescriptor<T> 
        implements DynamicParameterizedType {

    private Class<T> arrayObjectClass;

    @Override
    public void setParameterValues(Properties parameters) {
        arrayObjectClass = ( (ParameterType) parameters
            .get( PARAMETER_TYPE ) )
            .getReturnedClass();

    }

    public AbstractArrayTypeDescriptor(Class<T> arrayObjectClass) {
        super( 
            arrayObjectClass, 
            (MutabilityPlan<T>) new MutableMutabilityPlan<Object>() {
                @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> X unwrap(
            T value, 
            Class<X> type, 
            WrapperOptions options
        ) {
        return (X) ArrayUtil.wrapArray( value );
    }

    @Override
    public <X> 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
}