#java #hibernate #jpa
#java #спящий режим #jpa
Вопрос:
У меня есть древовидная структура, определенная в режиме гибернации. Где у меня есть абстрактный элемент с именем TreeObject. Объект дерева может иметь несколько дочерних элементов и только одного родителя.
У меня также есть некоторая реализация для этого класса: форма, категория, группа вопросов и вопрос. Все они наследуются от TreeObject .
Идея заключается в том, что форма может иметь в качестве дочерних категорий и вопросов. Категория может иметь в качестве дочерних групп и вопросов, а группы могут иметь в качестве дочерних другие группы и вопросы.
Затем я определил TreeObject как:
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class TreeObject{
@ManyToOne
private TreeObject parent;
@OneToMany(mappedBy = "parent")
private List<TreeObject> children;
[...]
}
Тогда другие объекты очень просты. Например, элемент формы:
@Entity
public class Form extends TreeObject {
[...]
}
И другие элементы аналогичны, за исключением некоторого нерелевантного кода для этого вопроса.
Проблема возникает, когда я хочу получить дочерние элементы элемента (поскольку TreeObject не является реальной таблицей в базе данных). Например, чтобы получить все дочерние элементы формы, hibernates создает кратное объединение из таблиц Form
, Category
, Group
, Question
для представления TreeObject
эквивалентности таблицы и выбора дочерних элементов. Когда в базе данных несколько элементов (но не так много), получение дочерних элементов может занять около 0,5 секунды из-за множественного объединения. Затем, когда у меня будет большой объем данных, у меня будут большие проблемы с производительностью этого запроса.
Например, пример запроса, полученного для получения формы:
select form0_.ID as ID1_7_0_, form0_.createdBy as createdB3_7_0_, form0_.name as name2_12_0_, form0_.parent_ID as parent_I5_12_0_, children1_.parent_ID as parent_I5_7_1_, children1_.ID as ID1_12_1_, children1_.ID as ID1_7_2_, children1_.createdBy as createdB3_7_2_, children1_.name as name2_12_2_, children1_.parent_ID as parent_I5_12_2_, children1_.version as version2_2_2_, children1_.clazz_ as clazz_2_, questionva2_.BaseQuestionWithValue_ID as BaseQues1_7_3_, questionva2_.questionValues as question2_38_3_, treeobject3_.ID as ID1_7_4_, treeobject3_.comparationId as comparat2_7_4_, treeobject3_.name as name2_12_4_, treeobject3_.parent_ID as parent_I5_12_4_, treeobject3_.clazz_ as clazz_4_ from form form0_ left outer join
( select ID, name, parent_ID, 10 as clazz_ from questions
union select ID, name, parent_ID, 24 as clazz_ from group
union select ID, name, parent_ID, 32 as clazz_ from category
union select ID, name, parent_ID, 26 as clazz_ from form )
children1_ on form0_.ID=children1_.parent_ID left outer join
question_with_values questionva2_ on children1_.ID=questionva2_.BaseQuestionWithValue_ID left outer join
( select ID, name, parent_ID, 10 as clazz_ from questions
union select ID, name, parent_ID, 24 as clazz_ from group
union select ID, name, originalReference, parent_ID, 32 as clazz_ fromcategory
union select ID, comparationId, name, parent_ID, 26 as clazz_ from form )
treeobject3_ on form0_.parent_ID=treeobject3_.ID where form0_.ID=344820 order by children1_.sortSeq asc;
(примечание: я удалил несколько столбцов, чтобы упростить понимание кода)
Теперь я использую @BatchSize
для повышения производительности, и общая производительность приложения улучшилась, но по-прежнему не является реальным решением.
Моя идея состоит в том, чтобы использовать что-то вроде @WhereJoinTable
фильтрации «большого» запроса объединения и извлекать только реальные дочерние элементы из категории и вопросов, а не все из них, избегая проблемы с производительностью. Но поскольку дочерний параметр mappedBy by parent
, я понятия не имею, как я могу этого добиться.
@ManyToOne
@JoinColumn(name="parent_ID")
@WhereJoinTable(clause=" ???? ")
private TreeObject parent;
Возможно, с @Filter
опцией гибернации:
@ManyToOne
@JoinColumn(name="parent_ID")
@Filter(name="parentGroup",condition=" ???? ")
private TreeObject parent;
Конечно, другим решением является изменение InheritanceType.TABLE_PER_CLASS
, чтобы иметь только одну большую таблицу, и поэтому union
она не будет отображаться в запросе. Но базу данных будет очень трудно читать, и я хочу этого избежать.
Вопрос в том, существует ли какой-либо способ улучшить производительность гибернации для извлечения всех дочерних элементов TreeObject?
Комментарии:
1. Не могли бы вы опубликовать сгенерированный SQL?
2. Да, конечно. Я отредактировал SQL, чтобы сделать его немного отсортированным, удалив некоторые столбцы, которые бесполезны для этого вопроса.
Ответ №1:
По ManyToOne
умолчанию используется аннотация EAGER
, и я думаю, вам действительно не нужен родительский элемент объектов, которые вы загружаете напрямую (через первичный ключ), верно?
Вы можете изменить ассоциацию следующим образом:
@ManyToOne(fetch = FetchType.LAZY)
private TreeObject parent;
Это должно, по крайней мере, удалить последнее соединение с объединениями.
Но из-за характера вашей модели (это рекурсия) вы никогда не сможете выбрать весь граф объектов без собственного запроса, потому что JPA просто не поддерживает его. Даже собственный запрос может быть не лучшим решением, и я думаю, вам следует рассмотреть возможность использования хранилища документов, которое позволяет хранить / загружать целые графики за одну операцию.
Комментарии:
1. Что вы подразумеваете под «использованием хранилища документов»?
2. @JorgeHortelano Взгляните на en.wikipedia.org/wiki/Document-oriented_database и en.wikipedia.org/wiki /… в частности.
3. Спасибо за информацию. Тем не менее, я бы предпочел работать с моей текущей базой данных MySQL, пока все возможности не будут отброшены. Если
@Where
or@Filter
не помогает, как вы предложили, тогда мне нужно найти другой подход. Но я хочу углубиться в свои знания JPA / hibernate и хочу посмотреть, есть ли какое-либо лучшее решение. Конечно, другим решением было бы экспортироватьForm
объект в JSON и сохранить его в MySQL в виде чистого текста, что позволит избежать всех моих проблем. Но это не научит меня ничему новому о спящем режиме.4. @JorgeHortelano Если вы хотите изучить JPA / Hibernate, вы правы, хранилище документов не поможет. Вы пробовали
FetchType.LAZY
, как я упоминал? Как я уже сказал, это должно снизить сложность вашего конкретного оператора SQL на ~ 50%.5. Мне очень нравится ваш ответ, потому что я сосредоточен,
@Where
но только сFetchType.LAZY
тегом производительность улучшается примерно на 20% в приложении. Это большое улучшение с небольшим изменением. Я боюсь, что это единственное возможное улучшение запроса, как вы предложили, или, может быть, также объединения могут быть уменьшены в размере с помощью другой «настройки».