Как зарегистрировать несколько отображений JDB C Types.OTHER при построении скалярных проекций для запросов Hibernate native SQL - PullRequest
1 голос
/ 06 апреля 2020

Я хочу сделать собственную скалярную проекцию в спящем режиме (с Tuple.class), используя как UUID, так и тип Range из hibernate-types-52 (@Vlad Mihalcea).

Я использую:

  • пружинная загрузка (v2.2.6.RELEASE)
  • hibernate (v5.4.12.Final)
  • hibernate -types-52 (v2.9.7)
  • postgres (v11)

Я объявил пользовательский PostgreSQL диалект

class CustomPostgreSQLDialect : PostgreSQL10Dialect() {

    init {
        this.registerColumnType(Types.BLOB, "bytea");
        this.registerHibernateType(Types.OTHER, PostgresUUIDType::class.java.name)
        //this.registerHibernateType(Types.OTHER, PostgreSQLRangeType::class.java.name)
    }

    override fun remapSqlTypeDescriptor(sqlTypeDescriptor: SqlTypeDescriptor): SqlTypeDescriptor {
        return if (sqlTypeDescriptor.sqlType == Types.BLOB) {
            BinaryTypeDescriptor.INSTANCE
        } else super.remapSqlTypeDescriptor(sqlTypeDescriptor)
    }
}

Когда следующая строка - комментарий

this.registerHibernateType(Types.OTHER, PostgreSQLRangeType::class.java.name)

Работает нормально, если и только если я приведу столбец диапазона.

val statement = "SELECT uuidColumn, CAST(rangeColumn as TEXT) FROM ...."
entityManager.createNativeQuery(statement, Tuple::class.java)

Если я прав, я могу объявить только одно сопоставление для java. sql .Types.OTHER, если я объявляю multi one, используется последний. В самом деле, если оба являются uncomment, у меня есть ClassCastException для UUID не может быть приведен к PGObject.

Есть ли способ сделать собственную скалярную проекцию (используя Tuple.class) с типами UUID и Range без необходимости приведения того или иного типа? Наконец, я хотел бы написать свой запрос следующим образом:

val statement = "SELECT uuidColumn, rangeColumn FROM ...."
entityManager.createNativeQuery(statement, Tuple::class.java)

Мне известны следующие опции

@SqlResultSetMapping(
  name="ProjectioName",
  columns=[
    ColumnResult(name="uuidColumn", type = PostgresUUIDType::class),
    ColumnResult(name="rangeColumn", type = PostgreSQLRangeType::class)
  ]
)

entityManager.createNativeQuery(statement, "ProjectioName")

Проблема в том, что я не хочу объявлять 'SqlResultSetMapping' для всех мои прогнозы с UUID и другим конкретным c столбцом (JSON, Range, ...)

или

entityManager.createNativeQuery(statement, Tuple::class.java)
  .unwrap(NativeQuery::class.java)
  .addScalar("uuidColumn", PostgresUUIDType.INSTANCE)
  .addScalar("rangeColumn", PostgreSQLRangeType.INSTANCE)

Эта опция может быть хорошо, но у меня много столбцов, если я начинаю добавлять «.addScalar (...)», мне нужно определить все столбцы. В идеале, я хотел бы использовать «.addScalar (...)» для некоторых определенных c столбцов и отступить к «стратегии» по умолчанию для тех, кого я не объявляю, возможно?

РЕДАКТИРОВАТЬ

Что-то, что я не упомянул как обязательное, - это моя потребность сделать собственный запрос, из-за некоторых (много) запросов, использующих CTE или Windows Функция или Рекурсивный запрос или спецификация c Postgres операторов .... Criteria API нельзя использовать для собственных запросов, верно?

Возможно использовать .addScalar (...) для некоторых определенных c столбцов (UUID, Range , JSON, ...) и другие столбцы в запросе не объявляются с помощью .addScalar () откат до разрешения по умолчанию?

Я хочу лучшее из двух миров, может быть, это утопия, но быть в состоянии указать только тип с ".addScalar" для некоторых столбцов, а другие продолжают работать как без ".addScalar" было бы здорово.

Ответы [ 2 ]

0 голосов
/ 10 апреля 2020

Возможно, вам следует попробовать Blaze-Persistence, которая позволит вам придерживаться своей модели JPA, но при этом писать расширенные SQL, например, например, рекурсивные CTE, операции над множествами и т.д. c. С Entity-Views вы можете даже упростить создание DTO. Это может выглядеть следующим образом:

CriteriaBuilder<MyCte> cb = criteriaBuilderFactory.create(entityManager, MyCte.class)
  .withRecursive(MyCte.class)
    .from(Book.class, "b")
    .bind("uuid").select("b.uuid")
    .bind("range").select("NULL")
    .where("b.id").eq(1)
  .unionAll()
    .from(Book.class, "b")
    .from(MyCte.class, "cte")
    .bind("uuid").select("b.uuid")
    .bind("range").select("NULL")
    // Your logic
  .end();
List<MyDto> myDtos = entityViewManager.applySetting(EntityViewSetting.create(MyDto.class), cb)
  .getResultList();

@CTE
@Entity
public class MyCte {
  @Id UUID uuid;
  Range range;
}

@EntityView(MyCte.class)
interface MyDto {
  UUID getUuid();
  Range getRange();
}

И будет производить запрос, подобный этому

WITH RECURSIVE MyCte(uuid, range) AS(
  SELECT b.uuid, null FROM book b
  UNION ALL
  SELECT b.uuid, null FROM book b, MyCte cte --your logic
)
SELECT m.uuid, m.range FROM MyCte m
0 голосов
/ 10 апреля 2020

Это очень распространенный вопрос, поэтому этот ответ основан на этой статье , которую я написал ранее.

Проблема

JDB C Types interface определяет все распространенные типы столбцов SQL, например VARCHAR, INTEGER, DATE.

Однако для базы данных c столбцы, у вас есть только запись Types.OTHER для ссылки на них.

Итак, если одна из ваших таблиц определяет и столбец UUID , и Range затем, когда вы выбираете строки из этой таблицы, оба этих столбца будут использовать тип Types.OTHER JDB C.

Таким образом, вы не можете зарегистрировать тип спящего по умолчанию для покрытия Types.OTHER, так как вам нужны разные типы в зависимости от специфики базы данных c тип столбца.

Решение

Вам необходимо явно указать Hibernate Type на уровне запроса:

List<Tuple> tuples = entityManager
.createNativeQuery(
    "SELECT uuidColumn, CAST(rangeColumn as TEXT) " +
    "FROM book ", Tuple.class)
.unwrap(org.hibernate.query.NativeQuery.class)
.addScalar("uuidColumn", PostgresUUIDType.INSTANCE)
.addScalar("rangeColumn", PostgreSQLRangeType.INSTANCE)
.getResultList();

Теперь ваша проблема заключалась в том, что:

Эта опция могла бы подойти, но у меня есть много столбцов, если я начну добавлять ".addS calar (...) ", мне нужно определить все столбцы.

И, есть решение.

Вы можете использовать Criteria API для построения Запросы динамически. Основываясь на метамодели сущности , вы можете применить правильный Hibernate Type при построении проекционного запроса.

Как насчет собственных SQL запросов?

Если вы хотите для динамического создания собственного SQL запроса вы можете использовать jOOQ, как описано в этой статье .

Заключение

В настоящее время не существует готового решения чтобы решить вашу проблему. Вам необходимо создать шаблонный метод DAO, который упрощает отображение проекций Types.OTHER, независимо от того, используете ли вы Criteria API или jOOQ.

...