Я вижу проблемы с производительностью при получении нескольких экземпляров объектов, которые имеют много связей с другими объектами. Я использую Spring и реализацию JPA Hibernate с MySQL. Проблема заключается в том, что при выполнении запроса JPA Hibernate не присоединяется автоматически к другим таблицам. Это приводит к n * r + 1 SQL-запросам, где n - количество извлекаемых объектов, а r - количество взаимосвязей.
Например, человек живет по адресу, имеет много хобби и побывал во многих странах:
@Entity
public class Person {
@Id public Integer personId;
public String name;
@ManyToOne public Address address;
@ManyToMany public Set<Hobby> hobbies;
@ManyToMany public Set<Country> countriesVisited;
}
Когда я выполняю запрос JPA, чтобы получить всех людей с именем Боб, и в базе данных есть 100 Бобов:
SELECT p FROM Person p WHERE p.name='Bob'
Hibernate переводит это в 301 SQL-запросов:
SELECT ... FROM Person WHERE name='Bob'
SELECT ... FROM Address WHERE personId=1
SELECT ... FROM Address WHERE personId=2
...
SELECT ... FROM Hobby WHERE personId=1
SELECT ... FROM Hobby WHERE personId=2
...
SELECT ... FROM Country WHERE personId=1
SELECT ... FROM Country WHERE personId=2
...
Согласно Hibernate FAQ ( здесь и здесь ), решение состоит в том, чтобы указать LEFT JOIN или LEFT OUTER JOIN (для многих ко многим) в запросе. Теперь мой запрос выглядит так:
SELECT p, a, h, c FROM Person p
LEFT JOIN p.address a LEFT OUTER JOIN p.hobbies h LEFT OUTER JOIN p.countriesVisited c
WHERE p.name = 'Bob'
Это работает, но, по-видимому, возникает ошибка, если существует более одного LEFT OUTER JOIN, и в этом случае Hibernate неправильно ищет несуществующий столбец:
could not read column value from result set: personId69_2_; Column 'personId69_2_' not found.
Поведение ошибки, возможно, устраняется с помощью Ошибка ядра Hibernate HHH-3636 . К сожалению, исправление не является частью какого-либо выпущенного Hibernate JAR. Я запустил свое приложение против сборки моментального снимка, но поведение ошибки все еще присутствует. Я также собрал свой собственный Hibernate Core JAR из последнего кода в хранилище, и поведение ошибки все еще присутствует. Так что, возможно, HHH-3636 не решает эту проблему.
Это ограничение производительности Hibernate очень расстраивает. Если я запрашиваю 1000 объектов, то в базу данных поступает 1000 * r + 1 SQL-запросов. В моем случае у меня 8 отношений, поэтому я получаю 8001 SQL-запрос, что приводит к ужасной производительности. Официальное решение Hibernate для этого состоит в том, чтобы оставить все отношения. Но это невозможно с более чем одним отношением «многие ко многим» из-за ошибки в поведении. Так что я застрял с левыми соединениями для отношений многие-к-одному и n * r + 1 запросов из-за отношений многие-ко-многим. Я планирую представить проблему LEFT OUTER JOIN как ошибку Hibernate, но в то же время моему клиенту нужно приложение с разумной производительностью. В настоящее время я использую комбинацию пакетной выборки (BatchSize), ehcache и пользовательского кэширования в памяти, но производительность все еще довольно низкая (улучшено получение 5000 объектов за 30–8 секунд). Суть в том, что слишком много SQL-запросов попадают в базу данных.
Итак, мои вопросы, возможно ли использовать Hibernate в чувствительных к производительности приложениях, где таблицы имеют несколько взаимосвязей друг с другом? Мне бы очень хотелось услышать, насколько успешно Hibernate использует производительность адресов. Должен ли я писать SQL вручную (что несколько противоречит цели использования Hibernate)? Нужно ли отменять нормализацию схемы базы данных, чтобы уменьшить количество соединяемых таблиц? Разве я не должен использовать Hibernate, если мне нужна высокая производительность запросов? Есть ли что-то быстрее?