Отображение массива с помощью Hibernate - PullRequest
7 голосов
/ 02 декабря 2010

Не могли бы вы помочь мне отобразить этот класс с помощью Hibernate?

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

Я использую PostgreSQL, а тип столбца в таблице - целое число [] Как должен быть сопоставлен мой массив?

Ответы [ 4 ]

15 голосов
/ 02 декабря 2010

Hibernate (и JPA) не может напрямую отобразить тип массива PostgreSQL. См. Этот вопрос , чтобы узнать, как действовать, если вам действительно нужно сохранить структуру базы данных такой, какая она есть. Эта тема содержит пример необходимого пользовательского типа.

Если вы можете изменить свою схему, вы можете позволить hibernate создать дополнительную таблицу для обработки коллекции - List<Integer>. Затем, в зависимости от версии hibernate, которую вы используете:

5 голосов
/ 08 октября 2015

Hibernate может отображать только примитивные типы.Проверьте в папке org.hibernate.type пакета jar пакета hibernate.массив 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();
5 голосов
/ 02 декабря 2010

Я никогда не отображал массивы в спящий режим.Я всегда использую коллекции.Итак, я немного изменил вам класс:

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;
    }   
    ...
0 голосов
/ 22 июня 2017

Как я объяснил в этой статье , для отображения массива с помощью 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...