#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 отдельными конечными точками:
/books-without-author/{id}
==> Возвращает книгу без автора/books-with-author-exception/{id}
==> Пытается получить книгу с автором, но выдает исключение из-за недоступности транзакции для загрузки прокси/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 попытается вставить две строки с разными уникальными идентификаторами.