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

Как сопоставить вычисляемые свойства с помощью аннотации Hibernate @Generated

Узнайте, как аннотация Hibernate @Generated может динамически задавать свойства сущности при вставке или обновлении на основе других значений свойств.

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

Вступление

Как я объяснил в этой предыдущей статье , вы можете сопоставить вычисляемые свойства с помощью Hibernate @Formula , и значение генерируется во время запроса.

В этом посте вы увидите, как можно рассчитать свойство сущности во время ВСТАВКИ или ОБНОВЛЕНИЯ.

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

Предполагая, что у нас есть следующее Герой сопоставление сущностей:

@Entity(name = "Hero")
public class Hero {

    @Id
    private Long id;

    private String firstName;

    private String lastName;

    private String middleName1;

    private String middleName2;

    private String middleName3;

    private String middleName4;

    private String middleName5;

    @Generated( value = GenerationTime.ALWAYS )
    @Column(columnDefinition =
        "AS CONCAT(" +
        "	COALESCE(firstName, ''), " +
        "	COALESCE(' ' + middleName1, ''), " +
        "	COALESCE(' ' + middleName2, ''), " +
        "	COALESCE(' ' + middleName3, ''), " +
        "	COALESCE(' ' + middleName4, ''), " +
        "	COALESCE(' ' + middleName5, ''), " +
        "	COALESCE(' ' + lastName, '') " +
        ")")
    private String fullName;

    //Getters and setters omitted for brevity

    public String getFullName() {
        return fullName;
    }
}

Свойство Полное имя вычисляется с помощью функции SQL Server CONCAT , включая все имена. Функция COALESCE используется, потому что CONCAT не использует разделитель в SQL Server.

Определение столбца полезно только при создании схемы базы данных из аннотаций JPA (что не следует делать в рабочей среде). Поскольку мы предоставили пользовательское @Определение столбца , базовая таблица базы данных выглядит следующим образом:

CREATE TABLE Hero
(
  id BIGINT NOT NULL ,
  firstName VARCHAR(255) ,
  fullName AS CONCAT(COALESCE(firstName, ''),
                     COALESCE(' ' + middleName1, ''),
                     COALESCE(' ' + middleName2, ''),
                     COALESCE(' ' + middleName3, ''),
                     COALESCE(' ' + middleName4, ''),
                     COALESCE(' ' + middleName5, ''),
                     COALESCE(' ' + lastName, '')) ,
  lastName VARCHAR(255) ,
  middleName1 VARCHAR(255) ,
  middleName2 VARCHAR(255) ,
  middleName3 VARCHAR(255) ,
  middleName4 VARCHAR(255) ,
  middleName5 VARCHAR(255) ,
  PRIMARY KEY ( id )
)

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

  • ВСТАВИТЬ – означает, что значение столбца вычисляется во время вставки
  • ВСЕГДА – означает, что значение столбца вычисляется как во время вставки, так и во время обновления

Время тестирования

Теперь, при сохранении Герой сущность:

doInJPA( entityManager -> {
    Hero heroine = new Hero();
    heroine.setId( 1L );

    heroine.setFirstName( "Agustina" );
    heroine.setMiddleName1( "Raimunda" );
    heroine.setMiddleName2( "María" );
    heroine.setMiddleName3( "Saragossa" );
    heroine.setLastName( "Domènech" );

    entityManager.persist( heroine );
    LOGGER.info("After entity persist action");
    entityManager.flush();

    assertEquals(
        "Agustina Raimunda María Saragossa Domènech", 
        heroine.getFullName()
    );
} );

Hibernate создает следующие инструкции SQL:

-- After entity persist action

INSERT INTO Hero 
    (firstName, lastName, middleName1, middleName2, middleName3, middleName4, middleName5, id) 
VALUES 
    ('Agustina', 'Domènech', 'Raimunda', 'María', 'Saragossa', NULL(VARCHAR), NULL(VARCHAR), 1)

SELECT 
    h.fullName as fullName3_0_ 
FROM Hero h 
WHERE h.id = 1

Обратите внимание на запрос SELECT , который выдается после операции flush , которая позволяет Hibernate извлекать вычисляемое свойство сущности.

При обновлении и загрузке объекта:

doInJPA( entityManager -> {
    Hero heroine = entityManager.find( Hero.class, 1L );
    heroine.setMiddleName1( null );
    heroine.setMiddleName2( null );
    heroine.setMiddleName3( null );
    heroine.setLastName( "de Aragón" );

    LOGGER.info("After entity update action");
    entityManager.flush();

    assertEquals("Agustina de Aragón", heroine.getFullName());
} );

Hibernate создает следующие инструкции SQL:

SELECT 
    h.id AS id1_0_0_,
    h.firstName AS firstNam2_0_0_,
    h.fullName AS fullName3_0_0_,
    h.lastName AS lastName4_0_0_,
    h.middleName1 AS middleNa5_0_0_,
    h.middleName2 AS middleNa6_0_0_,
    h.middleName3 AS middleNa7_0_0_,
    h.middleName4 AS middleNa8_0_0_,
    h.middleName5 AS middleNa9_0_0_
FROM Hero h
WHERE 
    h.id = 1

UPDATE Hero 
SET 
    firstName = 'Agustina', 
    lastName = 'de Aragón', 
    middleName1 = NULL(VARCHAR), 
    middleName2 = NULL(VARCHAR), 
    middleName3 = NULL(VARCHAR), 
    middleName4 = NULL(VARCHAR), 
    middleName5 = NULL(VARCHAR) 
WHERE 
    id = 1 
      
-- After entity update action

SELECT 
    h.fullName as fullName3_0_ 
FROM Hero h 
WHERE h.id = 1

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

Вывод

Сопоставление вычисляемых свойств объектов с помощью Hibernate довольно просто, и у вас есть несколько способов достижения этой цели.

Аннотация @Generated позволяет динамически создавать свойства объектов при вставке или обновлении на основе других значений свойств.