【问题标题】:How to map a 2-d matrix in Java to Hibernate/JPA?如何将 Java 中的二维矩阵映射到 Hibernate/JPA?
【发布时间】:2011-05-05 04:41:57
【问题描述】:

我有一个遗留数据库,我正试图重新设计到 21 世纪。现有数据结构之一涉及包含二维值矩阵的特定类。如果我要从数据库中对这个类进行逆向工程,我最终会得到一系列属性,例如:

private BigDecimal NODE_1_MATRIX_POS_1_1;
private BigDecimal NODE_1_MATRIX_POS_1_2;

等等。由于这是一个 6x6 矩阵,因此有很多这样的列。

我一直在寻找更好的方法,但我不确定我是否在那里。我想做的是这样的:

@Entity
public class TestClass {

    @Id
    private long id;

    @CollectionOfElements
    @JoinTable(
        name="MATRIX_DATA", 
        joinColumns=@JoinColumn(name="ENTRY_ID"))
    private List<List<BigDecimal>> matrix;

但这失败了:

org.hibernate.MappingException: Could not determine type for: java.util.List, at table: MATRIX_DATA, for columns: [org.hibernate.mapping.Column(element)]

与其只是尝试修复错误,我想我会四处打听并尝试找到解决此映射挑战的正确方法。有没有人通过 JPA 找到成功和满意的多维数组映射?

【问题讨论】:

  • 我不确定这里的最佳方法,但我认为要解决您的错误,您需要创建自己的 UserType

标签: java hibernate jpa multidimensional-array


【解决方案1】:

与其只是尝试修复错误,我想我会四处打听并尝试找到解决此映射挑战的正确方法。有没有人通过 JPA 找到成功和满意的多维数组映射?

AFAIK,标准 JPA 不支持嵌套集合。 JPA wiki book 有一个关于这个主题的好部分(我只引用了其中的一部分):

Nested Collections, Maps and Matrices

在对象中有些常见 具有复杂集合的模型 诸如List 之类的关系 Lists(即矩阵),或Map Maps,或MapLists,等等 在。不幸的是这些类型的 收藏品非常差地映射到 关系数据库。

JPA 不支持嵌套集合关系,通常 最好改变你的对象模型 避免他们做出坚持和 查询更容易。一种解决方案是 创建一个包装嵌套的对象 收藏。

例如,如果 Employee 有一个 MapProjects 由 String 项目类型和值 a ListProjects。映射这个 新的ProjectType 类可能是 创建用于存储项目类型和 OneToManyProject

...

这就是我的建议。例如:

@Entity
public class TestClass {    
    @Id
    private long id;

    @OneToMany(mappedBy="testClass")
    private List<MatrixRow> matrix;
}

MatrixLine 的位置(省略许多细节):

@Entity
public class MatrixRow {
    @Id
    private long id;

    @ManyToOne
    private TestClass testClass;

    @CollectionOfElements
    private List<BigDecimal> row;
}

或者也许您可以使用自定义用户类型(我不太确定这将如何工作)。

或者(毕竟,您已经在使用不可移植的注释)看看这个问题,看看如何扩展 Hibernate:

【讨论】:

  • 这正是我昨天晚些时候试图构建的那种东西。今天早上我会试一试,结合你的例子的不同之处。谢谢,帕斯卡!
【解决方案2】:

休眠类型项目

您可以使用 Hibernate Types 项目映射 PostgreSQL 多维数组。

您可以选择在实体属性端使用 Java 数组或使用List

数据库表

例如,假设您有以下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 实体

您可以使用EnumArrayType 映射seatGrid 列:

@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];
    }
}

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

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

@Type 注解允许您将参数传递给 Hibernate 类型,如 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'));

【讨论】:

  • 为一个 10 年前的问题添加一个新的(更好的)答案!有徽章吗? :-)
  • 那是持久性徽章?
  • 对我来说这看起来像是一个错字......你是说 PersistentBag 吗?
  • 当你把它当成玩笑来读它会更有意义?
猜你喜欢
  • 1970-01-01
  • 2012-12-10
  • 1970-01-01
  • 2014-02-21
  • 2013-10-30
  • 2018-04-11
  • 2012-04-06
  • 2016-05-16
  • 2013-05-16
相关资源
最近更新 更多