10 Stimmen

Wie kann man eine 2-d-Matrix in Java auf Hibernate/JPA abbilden?

Ich habe eine alte Datenbank, die ich für das 21. Jahrhundert umgestalten möchte. Eine der vorhandenen Datenstrukturen umfasst eine bestimmte Klasse, die eine 2-dimensionale Matrix von Werten enthält. Wenn ich diese Klasse aus der Datenbank zurückentwickeln würde, hätte ich am Ende eine Reihe von Attributen wie:

private BigDecimal NODE_1_MATRIX_POS_1_1;
private BigDecimal NODE_1_MATRIX_POS_1_2;

und so weiter. Da es sich um eine 6x6-Matrix handelt, gibt es eine Menge solcher Spalten.

Ich habe nach einem besseren Weg gesucht, aber ich bin mir nicht sicher, ob ich ihn gefunden habe. Was ich gerne tun würde, ist so etwas wie dieses:

@Entity
public class TestClass {

    @Id
    private long id;

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

Aber das scheitert:

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

Anstatt einfach zu versuchen, den Fehler zu beheben, dachte ich, ich frage mal herum und versuche, den richtigen Anfahrt zur Lösung dieser Kartierungsaufgabe. Hat jemand Erfolg und Zufriedenheit gefunden Mapping mehrdimensionale Arrays über JPA?

12voto

Pascal Thivent Punkte 548176

Anstatt nur zu versuchen, den Fehler zu beheben, dachte ich, ich würde herum fragen und versuchen, den richtigen Ansatz zur Lösung dieses Mapping Herausforderung zu finden. Hat jemand Erfolg und Zufriedenheit mit dem Mapping von mehrdimensionalen Arrays über JPA gefunden?

AFAIK, verschachtelte Sammlungen sind nicht von Standard-JPA unterstützt. Das JPA-Wiki-Buch hat einen guten Abschnitt zu diesem Thema (ich zitiere nur einen Teil davon):

Verschachtelte Sammlungen, Karten und Matrizen

In einem Objekt ist es durchaus üblich Modell ist es üblich, komplexe Sammlungs Beziehungen zu haben, wie zum Beispiel eine List o List s (d. h. eine Matrix) oder eine Map o Map s, oder eine Map von List s und so weiter. Unglücklicherweise Sammlungen sehr schlecht in einer relationalen Datenbank ab.

JPA unterstützt keine verschachtelten Sammlungsbeziehungen ist es am besten, Ihr Objektmodell zu ändern so zu ändern, dass sie vermieden werden, um die Persistenz und Abfragen zu erleichtern. Eine Lösung ist, ein ein Objekt zu erstellen, das die verschachtelte Sammlung umhüllt.

Wenn zum Beispiel ein Employee h Map von Project s String Projekttyp und die List ou Project s. Um dies abzubilden, muss eine neue ProjectType c erstellt werden, um den Projekttyp zu speichern und a OneToMany a Project .

...

Und das wäre mein Vorschlag. Zum Beispiel:

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

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

Dónde MatrixLine wäre (unter Auslassung vieler Details):

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

    @ManyToOne
    private TestClass testClass;

    @CollectionOfElements
    private List<BigDecimal> row;
}

Oder vielleicht Sie könnten einen benutzerdefinierten Benutzertyp verwenden (ich bin mir nicht ganz sicher, wie das funktionieren würde).

Oder (Sie verwenden ja bereits nicht portable Annotationen) schauen Sie sich diese Frage an, um zu sehen, wie Sie Hibernate erweitern können:

5voto

Vlad Mihalcea Punkte 121171

Hibernate-Typen-Projekt

Sie können ein mehrdimensionales PostgreSQL-Array mit der Methode Hibernate-Typen Projekt.

Sie können entweder ein Java-Array auf der Seite der Entitätsattribute verwenden oder die List .

Datenbank-Tabelle

Nehmen wir zum Beispiel an, dass Sie Folgendes haben plane Datenbank-Tabelle:

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

Wo die seat_status ist ein PostgreSQL-Enum:

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

JPA-Einheit

Sie können die seatGrid Spalte unter Verwendung der EnumArrayType :

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

Sie müssen also den zu verwendenden Hibernate-Typ deklarieren. Für Enums müssen Sie den EnumArrayType :

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

Les @Type Annotation ermöglicht die Übergabe von Parametern an den Hibernate-Typ, wie die SQL-Array-Klasse:

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

Prüfzeit

Wenn Sie nun auf Folgendes bestehen Post Einheit:

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 wird die richtige SQL INSERT-Anweisung ausgeben:

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

Und beim Abrufen der Entität funktioniert alles wie erwartet:

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'));

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X