Автор оригинала: 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.