使用Hibernate映射数组

dan*_*nik 9 java postgresql hibernate java-ee

你可以帮我映射一下Hbernate吗?

public class MyClass{
    private Long id;
    private String name;
    private int[] values;
    ...
}
Run Code Online (Sandbox Code Playgroud)

我正在使用PostgreSQL,表格的列类型是整数[]我的数组应该如何映射?

Boz*_*zho 15

Hibernate(和JPA)无法直接映射PostgreSQL数组类型.如果您确实需要保留数据库结构,请参阅此问题以了解如何继续.此线程具有所需自定义类型的示例.

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


Vla*_*cea 7

Maven 依赖

您需要做的第一件事是在您的项目配置文件中设置以下Hibernate Types Maven 依赖项pom.xml

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>${hibernate-types.version}</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

Maven 数组列

假设您的数据库中有此表:

create table event (
    id int8 not null, 
    version int4, 
    sensor_names text[], 
    sensor_values integer[], 
    primary key (id)
)
Run Code Online (Sandbox Code Playgroud)

你想像这样映射它:

@Entity(name = "Event")
@Table(name = "event")
@TypeDefs({
    @TypeDef(
        name = "string-array", 
        typeClass = StringArrayType.class
    ),
    @TypeDef(
        name = "int-array", 
        typeClass = IntArrayType.class
    )
})
public static class Event extends BaseEntity {
 
    @Type( type = "string-array" )
    @Column(
        name = "sensor_names", 
        columnDefinition = "text[]"
    )
    private String[] sensorNames;
 
    @Type( type = "int-array" )
    @Column(
        name = "sensor_values", 
        columnDefinition = "integer[]"
    )
    private int[] sensorValues;
 
    //Getters and setters omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

string-arrayint-array是可在被定义的自定义类型的BaseEntity超类:

@TypeDefs({
    @TypeDef(
        name = "string-array", 
        typeClass = StringArrayType.class
    ),
    @TypeDef(
        name = "int-array", 
        typeClass = IntArrayType.class
    )
})
@MappedSuperclass
public class BaseEntity {

    @Id
    private Long id;

    @Version
    private Integer version;

    //Getters and setters omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

StringArrayTypeIntArrayType由Hibernate的类型项目提供的课程。

测试时间

现在,当您插入几个实体时;

Event nullEvent = new Event();
nullEvent.setId(0L);
entityManager.persist(nullEvent);
 
Event event = new Event();
event.setId(1L);
event.setSensorNames(
    new String[] {
        "Temperature", 
        "Pressure"
    }
);
event.setSensorValues( 
    new int[] {
        12, 
        756
    } 
);
entityManager.persist(event);
Run Code Online (Sandbox Code Playgroud)

Hibernate 将生成以下 SQL 语句:

INSERT INTO event (
    version, 
    sensor_names, 
    sensor_values, 
    id
) 
VALUES (
    0, 
    NULL(ARRAY), 
    NULL(ARRAY), 
    0
)
     
INSERT INTO event (
    version, 
    sensor_names, 
    sensor_values, 
    id
) 
VALUES ( 
    0, 
    {"Temperature","Pressure"}, 
    {"12","756"}, 
    1
)
Run Code Online (Sandbox Code Playgroud)


Ale*_*exR 5

我从未将数组映射到休眠状态.我总是使用收藏品.所以,我稍微改变了你的课程:

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;
    }   
    ...
Run Code Online (Sandbox Code Playgroud)

  • 它不起作用.`引起:org.hibernate.AnnotationException:使用@OneToMany或@ManyToMany定位未映射的类 (6认同)
  • 为什么这个答案被接受了?您必须使用@ElementCollection或创建自定义类型。 (4认同)

use*_*369 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;
     }
 }
Run Code Online (Sandbox Code Playgroud)

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 };
}
Run Code Online (Sandbox Code Playgroud)

当您查询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();
Run Code Online (Sandbox Code Playgroud)