2010-12-02 20 views
7

¿Puede ayudarme a mapear la clase Hbernate?Mapping array con Hibernate

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

estoy usando PostgreSQL y el tipo de columna n de la mesa es entero [] Cómo debería corresponderse mi matriz?

Respuesta

6

Nunca he mapeado matrices para hibernar. Yo siempre uso colecciones. Por lo tanto, he cambiado ligeramente clase que:

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; 
    } 
    ... 
+3

No funciona. 'Causado por: org.hibernate.AnnotationException: uso de @OneToMany o @ManyToMany para una clase no asignada' –

12

Hibernate (y JPA) no puede asignar directamente el tipo de matriz PostgreSQL. See this question para saber cómo proceder si realmente necesita conservar la estructura de su base de datos tal como está. This thread tiene un ejemplo del tipo personalizado requerido.

Si puede cambiar su esquema, puede dejar que hibernate cree una tabla adicional para manejar la colección - List<Integer>. Luego, dependiendo de la versión de hibernación que está utilizando:

5

Hibernate puede asignar sólo los tipos primitivos. Verifique en la carpeta org.hibernate.type del paquete hibernate jar. int array no es uno de ellos. Por lo tanto, tendría que escribir un tipo personalizado que pueda implementar la interfaz 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 }; 
} 

Cuando se consulta a la entidad MiClase puede añadir algo como esto:

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(); 
+0

¿Lo usa en producción? ¿Esto funciona para tí? – corsiKa

+0

Sí, está trabajando en producción. He copiado este fragmento de mi código de trabajo – user3820369

0

Como expliqué en this article, la cartografía de una matriz con Hibernate requiere un tipo personalizado.

Así, suponiendo que define el IntArrayType así:

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); 
    } 
} 

También necesita la 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 
       ); 
      } 
     }; 
    } 
} 

Y el 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"; 
    } 
} 

La mayor parte de Java el manejo del tipo to-JDBC está incluido en el AbstractArrayTypeDescriptor clase de base:

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 se basa en ArrayUtil para manejar la copia completa gama de Java, envolver y desenvolver la lógica.

Ahora, estás mapeo se verá así:

@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 
}