Учитывая следующую модель предметной области, я хочу загрузить все Answer
s, включая их Value
и соответствующие дочерние элементы, и поместить их в, AnswerDTO
чтобы затем преобразовать в JSON. У меня есть рабочее решение, но оно страдает от проблемы N + 1, от которой я хочу избавиться, используя специальное решение @EntityGraph
. Все ассоциации настроены LAZY
.
@Query("SELECT a FROM Answer a")
@EntityGraph(attributePaths = {"value"})
public List<Answer> findAll();
Использование Однорангового @EntityGraph
на Repository
методе , который я могу гарантировать , что значения предварительно натянутые предотвратить N +-на Answer->Value
ассоциации. Хотя мой результат в порядке, есть еще одна проблема N + 1, из-за ленивой загрузки selected
ассоциации MCValue
s.
Используя это
@EntityGraph(attributePaths = {"value.selected"})
терпит неудачу, потому что selected
поле, конечно, является только частью некоторых Value
объектов:
Unable to locate Attribute with the the given name [selected] on this ManagedType [x.model.Value];
Как я могу сказать JPA только попытаться получить selected
ассоциацию, если значение равно MCValue
? Мне нужно что - то вроде optionalAttributePaths
.
источник
selected
тех ответов, которые имеютMCValue
. Мне не понравилось, что для этого потребуется дополнительный цикл, и мне нужно будет управлять отображением между наборами данных. Мне нравится ваша идея использовать кеш Hibernate для этого. Можете ли вы уточнить, насколько безопасно (с точки зрения согласованности) полагаться на кэш для хранения результатов? Это работает, когда запросы сделаны в транзакции? Я боюсь трудно заметить и случайные ленивые ошибки инициализации.MCValue
сущности. И вам не нужен дополнительный цикл. Вы должны выбрать всеMCValue
объекты сAnswer
одним запросом, который присоединяется к и использует то же предложение WHERE, что и текущий запрос. Я также говорил об этом в сегодняшнем прямом эфире: youtu.be/70B9znTmi00?t=238 Это началось в 3:58, но я взял несколько других вопросов между ними ...SINGLE_TABLE_INHERITANCE
.Я не знаю, что там делает Spring-Data, но для этого обычно нужно использовать
TREAT
оператор, чтобы получить доступ к подассоциации, но реализация для этого оператора довольно глючная. Hibernate поддерживает неявный доступ к свойствам подтипов, который вам здесь нужен, но, очевидно, Spring-Data не может справиться с этим должным образом. Я могу порекомендовать вам взглянуть на Blaze-Persistence Entity-Views , библиотеку, которая работает поверх JPA и позволяет сопоставлять произвольные структуры с вашей моделью сущностей. Вы можете отобразить свою модель DTO безопасным способом, а также структуру наследования. Представления сущностей для вашего варианта использования могут выглядеть следующим образомБлагодаря интеграции данных Spring, предоставляемой Blaze-Persistence, вы можете определить репозиторий, подобный этому, и напрямую использовать результат
Он сгенерирует HQL-запрос, который выбирает только то, что вы отобразили в
AnswerDTO
что-то вроде следующего.источник
interface MCValueDTO extends ValueDTO { @Mapping("selected.id") Set<Long> getOption(); }
Мой последний проект использовал GraphQL (первый для меня), и у нас была большая проблема с N + 1 запросами и попыткой оптимизировать запросы так, чтобы они объединялись только для таблиц, когда они необходимы. Я нашел Cosium / spring-data-jpa-entity-graph незаменимым. Он расширяет
JpaRepository
и добавляет методы для передачи графа сущностей в запрос. Затем вы можете строить динамические графы сущностей во время выполнения, чтобы добавлять в левые объединения только те данные, которые вам нужны.Наш поток данных выглядит примерно так:
Чтобы решить проблему не включения недопустимых узлов в граф сущностей (например,
__typename
из graphql), я создал служебный класс, который обрабатывает генерацию графа сущностей. Вызывающий класс передает имя класса, для которого он генерирует граф, который затем проверяет каждый узел в графе на соответствие метамодели, поддерживаемой ORM. Если узел отсутствует в модели, он удаляет его из списка узлов графа. (Эта проверка должна быть рекурсивной и проверять каждого ребенка)Прежде чем найти это, я попробовал проекции и любые другие альтернативы, рекомендованные в документах Spring JPA / Hibernate, но, похоже, ничто не решило проблему элегантно или, по крайней мере, с помощью тонны дополнительного кода.
источник
selected
ассоциация доступна не для всех подтиповvalue
.Отредактировано после вашего комментария:
Приношу свои извинения, я не справился с вашей проблемой в первом раунде, ваша проблема возникает при запуске данных Spring, а не только при попытке вызвать findAll ().
Итак, теперь вы можете перемещаться по полному примеру, который можно взять из моего github: https://github.com/bdzzaid/stackoverflow-java/blob/master/jpa-hibernate/.
Вы можете легко воспроизвести и исправить вашу проблему в этом проекте.
В действительности, данные Spring и спящий режим не способны определять «выбранный» график по умолчанию, и вам необходимо указать способ сбора выбранного параметра.
Итак, сначала вы должны объявить NamedEntityGraphs класса Ответ
Как видите, есть два NamedEntityGraph для значения атрибута класса Answer
Первое для всех значение без конкретного отношения к загрузке
Второй для определенного значения Multichoice . Если вы удалите это, вы воспроизведете исключение.
Во-вторых, вам нужно быть в транзакционном контексте answerRepository.findAll (), если вы хотите получать данные типа LAZY
источник
value
-ассоциации,Answer
а в полученииselected
ассоциации в случае, еслиvalue
естьMCValue
. Ваш ответ не содержит никакой информации об этом.OneToMany
как,FetchType.EAGER
но как указано в вопросе: все ассоциации естьLAZY
.selected
для каждого ответа вместо их предварительной загрузки.