2010-12-02 5 views
7

Pouvez-vous s'il vous plaît m'aider à mapper la classe Hbernate?Mapping array avec Hibernate

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

J'utilise PostgreSQL et le type de colonne n la table est entier [] Comment mon tableau doit être mis en correspondance?

Répondre

6

Je n'ai jamais mappé des tableaux pour hiberner. J'utilise toujours des collections. Alors, je l'ai un peu changé, vous classe:

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

Il ne fonctionne pas. 'Causé par: org.hibernate.AnnotationException: Utilisation de @OneToMany ou @ManyToMany ciblant une classe non mappée' –

12

Mise en veille prolongée (et JPA) ne peut pas mapper directement le type de tableau PostgreSQL. See this question pour savoir comment procéder si vous avez vraiment besoin de conserver votre structure de base de données telle qu'elle est. This thread a un exemple du type personnalisé requis.

Si vous pouvez modifier votre schéma, vous pouvez laisser Hibernate créer une table supplémentaire pour gérer la collection - List<Integer>. Ensuite, en fonction de la version de mise en veille prolongée, vous utilisez:

5

Mise en veille prolongée peut seulement mapper les types primitifs. Vérifiez sous le dossier org.hibernate.type du package hibernate jar. int array n'en fait pas partie. Vous devrez donc écrire un type personnalisé pouvant implémenter l'interface 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 }; 
} 

Lorsque vous interrogez pour l'entité MyClass vous pouvez ajouter quelque chose comme ceci:

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

Utilisez-vous ceci dans la production? Est-ce que ça marche pour toi? – corsiKa

+0

Oui, il travaille dans la production. J'ai copié cet extrait de mon code de travail – user3820369

0

Comme je l'ai expliqué dans this article, un tableau avec la cartographie Hibernate nécessite un type personnalisé.

Donc, en supposant que définir le IntArrayType comme ceci:

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

Vous devez aussi 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 
       ); 
      } 
     }; 
    } 
} 

Et le 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 majeure partie de Java- La manipulation de type to-JDBC est incluse dans le AbstractArrayTypeDescriptor classe 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 repose sur ArrayUtil pour gérer le tableau Java copie en profondeur, d'emballage et déballant logique.

Maintenant, vous êtes la cartographie ressemblera à ceci:

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