2010-12-02 127 views
7

能否请你帮我个映射类Hbernate?映射数组与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以未映射的类为目标` – 2015-09-22 10:36:35

12

Hibernate(和JPA)不能直接映射PostgreSQL数组类型。 See this question如果您确实需要保留您的数据库结构,那么该如何继续。 This thread有一个所需自定义类型的示例。

如果你可以改变你的模式,你可以让Hibernate创建一个额外的表来处理收集 - List<Integer>。然后,根据版本休眠您使用:

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

您是否在生产中使用这个?这对你有用吗? – corsiKa 2015-12-24 03:24:50

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类型处理包含在中的基类:

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 
}