Отложенное свойство выборки QueryDSL

#hibernate #kotlin #jpa #querydsl

#переход в спящий режим #kotlin #jpa #querydsl

Вопрос:

У меня есть свойство, которое обычно извлекается лениво:

 @Entity
class Version(...,
              @Basic(fetch = FetchType.LAZY)
              @Type(type = "text")
              var mappings: String? = null) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private val id = 0
}
  

Но иногда мне нужно с нетерпением его извлекать. Как я могу это сделать с помощью QueryDSL?
Это то, что у меня сейчас есть:

 JPAQuery<Any>(entityManager).from(QVersion.version)
        .where(...)
        .select(QVersion.version)
        .fetchOne()
  

Но это приводит к исключению, когда я пытаюсь получить доступ к свойству позже:

org.спящий режим.Исключение LazyInitializationException: невозможно выполнить запрошенную отложенную инициализацию [Version.mappings] — нет сеанса, и настройки запрещают загрузку вне сеанса

Комментарии:

1. Я полагаю, что вы настроили улучшение байт-кода, чтобы иметь возможность ленивой загрузки простого поля (а не отношения сущности). Возможно, я совершенно не прав; но весь смысл этого заключается в том, чтобы убедиться, что поле загружено лениво. Если вам нужно иногда загружать его с нетерпением; Я бы рекомендовал извлечь поле в отдельный объект (с той же таблицей и идентификатором Version , конечно), т. е. VersionMappings Использовать entity relation вместо field. Таким образом, вы можете сделать что-то вроде быстрой загрузки отношения ленивых сущностей. from(QVersion.version).innerJoin(QVersion.version.mappings).fetchJoin().select(QVersion.version)

2. @feanor07 да, улучшение байт-кода включено. Это звучит как хорошее решение. Не могли бы вы превратить это в ответ с примерами двух объектов? Я добавил простой идентификатор в свой класс версии в вопросе.

3. Я добавлю рабочий пример для вас через пару часов; но это будет на Java, а не на kotlin; Я надеюсь, что это нормально: D

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

Ответ №1:

Для этого есть очень простое решение: .fetchAll()

 JPAQuery<Any>(entityManager).from(QVersion.version)
        .fetchAll()  // <- here
        .where(...)
        .select(QVersion.version)
        .fetchOne()
  

Связанная документация по переходу в спящий режим:

Если вы используете отложенную выборку на уровне свойств (с инструментарием байт-кода), можно заставить Hibernate немедленно извлекать отложенные свойства в первом запросе, используя выборку всех свойств.

Ответ №2:

Я бы предложил разделить таблицу на две отдельные сущности и определить lazy OneToOne связь, чтобы вы могли быстро ее извлекать QueryDSL , когда это необходимо. Итак, для вашего примера у вас могут быть следующие объекты:

 @Entity
@Table(name="version")
class Version{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private val id = 0;
    @OneToOne(optional = false, fetch = FetchType.LAZY)
    @JoinColumn(name = "id", referencedColumnName = "id")
    private VersionMappings mappings;
}

@Entity
@Table(name="version")
VersionMappings{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private val id = 0;
    @Basic(fetch = FetchType.LAZY)
    @Type(type = "text")
    var mappings: String? = null);
}
  

извините, если синтаксис неправильный; поскольку я не знаком Kotlin . Затем вы можете выполнить быструю выборку следующим образом:

     QVersion version = QVersion.version;

    Version versionWithEagerlyFetchedMapping = JPAQuery<>(entityManager).from(version).where(version.id.eq(id)).rightJoin(version.versionMapping).fetchJoin().select(version).fetchOne();
  

Я подготовил следующее репозиторий github, чтобы продемонстрировать свое предложение в действии. Репозиторий представляет собой приложение Spring Boot с 3 отдельными конечными точками:

  1. /books-without-author/{id} ==> Возвращает книгу без автора
  2. /books-with-author-exception/{id} ==> Пытается получить книгу с автором, но выдает исключение из-за недоступности транзакции для загрузки прокси
  3. /books-with-author/{id} ==> Получает книгу с автором с помощью QueryDSL быстрой выборки в соответствии с моим предложением выше.

Вы можете проверить журналы консоли, чтобы увидеть, как работает быстрая выборка. При вызове третьей конечной точки выше вы увидите следующий сгенерированный запрос: select book0_.id as id1_0_0_, bookauthor1_.id as id1_0_1_, book0_.name as name2_0_0_, book0_.publish_year as publish_3_0_0_, book0_.version as version4_0_0_, bookauthor1_.author as author5_0_1_ from book book0_ right outer join book bookauthor1_ on book0_.id=bookauthor1_.id where book0_.id=?

С другой стороны, первая конечная точка сгенерирует запрос типа select book0_.id as id1_0_0_, book0_.name as name2_0_0_, book0_.publish_year as publish_3_0_0_, book0_.version as version4_0_0_ from book book0_ where book0_.id=?

Комментарии:

1. Как мне вставить эту модель? Если я попытаюсь store использовать версию с помощью VersionMappings, hibernate попытается вставить две строки с разными уникальными идентификаторами.