2010-12-02 18 views
7

あなたはHibernateクラスをマップするのを手伝ってもらえますか?Hibernateを使用したマッピング配列

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

私は、PostgreSQLと列タイプを使用しているn個の表は、整数私の配列をマッピングする必要がありますどのように[] のですか?

答えて

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

。 '原因:org.hibernate。AnnotationException:マップされていないクラスを対象とした@OneToManyまたは@ManyToManyの使用 ' –

12

Hibernate(とJPA)はPostgreSQLの配列タイプを直接マッピングすることはできません。 See this questionあなたが実際にデータベース構造をそのまま保持する必要がある場合は、どのように処理を進めますか。 This threadには、必要なカスタムタイプの例があります。

スキーマを変更できる場合は、コレクションを処理するために追加のテーブルを作成することができます(List<Integer>)。次に、使用している冬眠のバージョンによって:

5

を使用しています。 hibernate jarパッケージのorg.hibernate.typeフォルダを確認してください。 int配列はそれらの1つではありません。したがって、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のエンティティを照会するときは、このような何かを追加することができます:私はthis articleで説明したように持つ配列をマッピングし、

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

これを運用環境で使用していますか?これはあなたのために働きますか? – corsiKa

+0

はい、本番環境で作業しています。このコードを自分の作業コードからコピーしました – user3820369

0

をHibernateにはカスタムタイプが必要です。

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ベースのバルクあなたがもArraySqlTypeDescriptor必要

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

だから、あなたはこのようなIntArrayTypeを定義すると仮定するとto-JDBC型の処理は、に含まれています回のベースクラス:

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は、Java配列の深いコピー、ラッピングやアンラップロジックを処理するためにArrayUtilに依存しています。

さて、あなたはマッピングは次のようになりますよ:それは動作しません

@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 
} 
関連する問題