Рубрики
Без рубрики

Сопоставление многомерных массивов с помощью JPA и гибернации

Узнайте, как лучше всего сопоставить атрибуты сущности JPA многомерного массива Java с помощью проекта с открытым исходным кодом Hibernate Hibernate Types.

Автор оригинала: Vlad Mihalcea.

Вступление

В этой статье я собираюсь представить лучший способ отображения многомерного массива Java при использовании JPA и Hibernate.

Хотя одномерные типы массивов уже давно поддерживаются проектом Hibernate Types с открытым исходным кодом, начиная с версии 2.9, теперь вы можете сохранять атрибуты сущности многомерного массива с помощью JPA и Hibernate.

Сопоставление многомерных массивов с помощью JPA и гибернации. #Java https://t.co/UNotbeqHTq pic.twitter.com/q2b4BrrG2q

Модель предметной области

Давайте предположим, что у нас есть приложение для бронирования мест в самолете, и каждый самолет представлен в базе данных с помощью следующей таблицы:

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

Обратите внимание, что столбец seat_grid имеет тип seat_status [] . seat_status – это перечисление PostgreSQL, которое было создано следующим образом:

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

И наше приложение использует следующий класс Plane entity:

Сопоставление Плоскости сущности выполняется следующим образом:

@Entity(name = "Plane")
@Table(name = "plane")
@TypeDef(
    name = "seat_status_array", 
    typeClass = EnumArrayType.class
)
public 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;

    public Long getId() {
        return id;
    }

    public Plane setId(Long id) {
        this.id = id;
        return this;
    }

    public String getName() {
        return name;
    }

    public Plane setName(String name) {
        this.name = name;
        return this;
    }

    public SeatStatus[][] getSeatGrid() {
        return seatGrid;
    }

    public Plane setSeatGrid(SeatStatus[][] seatGrid) {
        this.seatGrid = seatGrid;
        return this;
    }

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

Тип массива перечисления предоставляется проектом Типы гибернации , поэтому вам не нужно его реализовывать.

Важно отметить, что сопоставление атрибутов seat_grid сущности предоставляет тип столбца SQL, связанный с массивом базы данных. Это важно, поскольку типам гибернации необходимо знать тип базы данных при построении массива PostgreSQL, в котором задается значение столбца seat_grid .

Установщики Plane сущностей используют API Fluent style для упрощения процесса создания сущностей.

Сохранение многомерного массива с помощью JPA и гибернации

В текущем рейсе используется самолет ATR-42 с двумя турбовинтовыми двигателями, который имеет 12 рядов по 4 места в каждом ряду. При сохранении связанной Плоскости сущности:

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
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED, 
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED, 
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED, 
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED, 
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED, 
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED, 
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED, 
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                },
                {
                    SeatStatus.RESERVED, SeatStatus.RESERVED, 
                    SeatStatus.RESERVED, SeatStatus.RESERVED
                },
                {
                    SeatStatus.BLOCKED, SeatStatus.BLOCKED, 
                    SeatStatus.BLOCKED, SeatStatus.BLOCKED
                }
            }
        )
);

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"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"},
        {"RESERVED", "RESERVED", "RESERVED", "RESERVED"},
        {"BLOCKED", "BLOCKED", "BLOCKED", "BLOCKED"}
    }, 
    1
)

Извлечение многомерного массива с помощью JPA и гибернации

При извлечении объекта Plane мы видим, что атрибут сетка сидений объект выбран правильно:

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

Круто, правда?

Вывод

В то время как вы могли бы реализовать свой собственный Тип гибернации для поддержки массивов, предлагаемые проектом Типы гибернации очень сложны и позволяют хранить как одномерные, так и многомерные типы столбцов массива PostgreSQL.