我有一个遗留数据库,我正试图重新设计到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找到成功和满意度映射多维数组?
答案 0 :(得分:12)
我认为我不会试图修复错误,而是试图找到解决此映射挑战的正确方法。有没有人通过JPA找到成功和满意度映射多维数组?
AFAIK,标准JPA不支持嵌套集合。 JPA维基书有关于这个主题的很好的部分(我只引用了它的一部分):
Nested Collections, Maps and Matrices
在一个对象中有点常见 模型有复杂的集合 诸如
List
之类的关系List
s(即矩阵)或Map
Map
个,或Map
个List
个,等等 上。不幸的是这些类型 集合映射非常糟糕 关系数据库。JPA通常不支持嵌套集合关系 最好更改对象模型 避免它们使持久性和 查询更容易。一个解决方案是 创建一个包装嵌套的对象 集合。强>
例如,如果
Employee
有一个 由{a}Map
标记的Project
String
项目类型和值aList
或Project
。要映射这个 新的ProjectType
课程可以 创建用于存储项目类型和OneToMany
到Project
。...
这就是我的建议。例如:
@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:
答案 1 :(得分:1)
您可以使用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'
);
您可以如下映射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。