Java& PostgreSQL&休眠。映射多维数组

时间:2017-05-29 12:19:03

标签: java arrays postgresql hibernate multidimensional-array

您好!

我试图用Hibernate从PostgreSQL映射三维数组。需要存储多层神经网络的权重。我该怎么做得更好?

我见过的所有来源都太旧了。在我看来,有更方便的方法来应对它。

谢谢!!!

UPD:我需要Double [] [] []

2 个答案:

答案 0 :(得分:3)

这是一个非常常见的问题,所以我决定将其变成article

休眠类型项目

通过hibernate-types项目,您可以为JPA和Hibernate实体属性保留多维数组。

数据库表

因此,假设您具有以下plane数据库表:

CREATE TABLE plane (
    id INT8 NOT NULL,
    name VARCHAR(255),
    seat_grid seat_status[][],
    PRIMARY KEY (id)
)

其中seat_status是PostgreSQL枚举:

CREATE TYPE seat_status
AS ENUM (
    'UNRESERVED',
    'RESERVED',
    'BLOCKED'
);

JPA实体

您可以如下映射plane数据库表:

@Entity(name = "Plane")
@Table(name = "plane")
@TypeDef(
    name = "seat_status_array",
    typeClass = EnumArrayType.class
)
public static class Plane {

    @Id
    private Long id;

    private String name;

    @Type(
        type = "seat_status_array",
        parameters = @org.hibernate.annotations.Parameter(
            name = "sql_array_type",
            value = "seat_status"
        )
    )
    @Column(
        name = "seat_grid",
        columnDefinition = "seat_status[][]"
    )
    private SeatStatus[][] seatGrid;

    //Getters and setters omitted for brevity

    public SeatStatus getSeatStatus(int row, char letter) {
        return seatGrid[row - 1][letter - 65];
    }
}

因此,您需要声明要使用的适当的休眠类型。对于枚举,您需要使用EnumArrayType

@TypeDef(
    name = "seat_status_array",
    typeClass = EnumArrayType.class
)

@Type批注允许您将参数传递给Hibernate Type,例如SQL数组类:

@Type(
    type = "seat_status_array",
    parameters = @org.hibernate.annotations.Parameter(
        name = "sql_array_type",
        value = "seat_status"
    )
)

测试时间

现在,当您保留以下Post实体时:

entityManager.persist(
    new Plane()
        .setId(1L)
        .setName("ATR-42")
        .setSeatGrid(
            new SeatStatus[][] {
                {
                    SeatStatus.BLOCKED, SeatStatus.BLOCKED,
                    SeatStatus.BLOCKED, SeatStatus.BLOCKED
                },
                {
                    SeatStatus.UNRESERVED, SeatStatus.UNRESERVED,
                    SeatStatus.RESERVED, SeatStatus.UNRESERVED
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED,
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                }
            }
        )
);

Hibernate将发出正确的SQL INSERT语句:

INSERT INTO plane (
    name,
    seat_grid,
    id
)
VALUES (
    'ATR-42',
    {
        {"BLOCKED", "BLOCKED", "BLOCKED", "BLOCKED"},
        {"UNRESERVED", "UNRESERVED", "RESERVED", "UNRESERVED"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"}
    },
    1
)

而且,在获取实体时,一切都会按预期进行:

Plane plane = entityManager.find(Plane.class, 1L);

assertEquals("ATR-42", plane.getName());
assertEquals(SeatStatus.BLOCKED, plane.getSeatStatus(1, 'A'));
assertEquals(SeatStatus.BLOCKED, plane.getSeatStatus(1, 'B'));
assertEquals(SeatStatus.BLOCKED, plane.getSeatStatus(1, 'C'));
assertEquals(SeatStatus.BLOCKED, plane.getSeatStatus(1, 'D'));
assertEquals(SeatStatus.UNRESERVED, plane.getSeatStatus(2, 'A'));
assertEquals(SeatStatus.UNRESERVED, plane.getSeatStatus(2, 'B'));
assertEquals(SeatStatus.RESERVED, plane.getSeatStatus(2, 'C'));
assertEquals(SeatStatus.UNRESERVED, plane.getSeatStatus(2, 'D'));

有关此主题的更多详细信息,请查看this article

答案 1 :(得分:1)

您可以编写自己的 UserType 。它虽然是 postgresql特定的。我为二维数组(int [] [])写了一个,我相信你可以为3-di数组做同样的事情。

这是我的代码:

package kg.nsi.domain.customtypes;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;

import java.io.Serializable;
import java.sql.*;

/**
 * Created by erlan on 12/22/17.
 */

public class BiDirectionalIntArrayUserType implements UserType {
    private final int[] arrayTypes = new int[]{Types.ARRAY};

    @Override
    public int[] sqlTypes() {
        return arrayTypes;
    }

    @Override
    public Class<int[][]> returnedClass() {
        return int[][].class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return x == null ? y == null : x.equals(y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x == null ? 0 : x.hashCode();
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
            throws HibernateException, SQLException {
        if (names != null && names.length > 0 && rs != null && rs.getArray(names[0]) != null) {
            Object array = rs.getArray(names[0]).getArray();
            if (array instanceof int[][])
                return array;
            else
                return convertShortArrayToInt((Number[][]) array);
        }

        return null;
    }

    private int[][] convertShortArrayToInt(Number[][] array) {
        int[][] intArray = new int[array.length][array[0].length];
        for (int i = 0; i < array.length; i++)
            for(int j = 0; j < array[0].length; j++)
                intArray[i][j] = array[i][j].intValue();

        return intArray;
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
            throws HibernateException, SQLException {
        if (value != null && st != null) {
            int[][] intarray = (int[][]) value;
            Array array = session.connection().createArrayOf("integer", intarray);
            st.setArray(index, array);
        } else {
            st.setNull(index, arrayTypes[0]);
        }
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        if (value == null)
            return null;

        int[][] intarray = (int[][]) value;
        int[][] clone = new int[intarray.length][intarray[0].length];
        for (int i = 0; i < intarray.length; i++)
            for(int j = 0; j < intarray[0].length; j++)
                clone[i][j] = intarray[i][j];

        return clone;
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }
}