Оптимизация Hibernate @ ManyToOne / @ JoinColumn

У меня есть объект Hibernate, который состоит из многих других объектов, которые используются в приложении. Другие сущности, составляющие этот MainEntity, соединяются с помощью @ManyToOne и @JoinColumn. Этот класс MainEntity имеет 5 столбцов (@Column) и 7 используемых объектов @ManyToOne / @JoinColumn.

Мне кажется, что при получении всех этих классов MainEntity у меня возникают проблемы с производительностью. Мы хотим сериализовать MainEntity в JSON, а также другие связанные с ним сущности. Обратите внимание, что мы извлекаем не так уж много - всего менее 30.

Ниже приведен пример того, как выглядит класс вместе с моим методом findAll() для получения этих классов. Я знаю, что @ManyToOne - это EAGER по умолчанию, поэтому мне интересно, есть ли лучший способ получить все эти объекты, который проще в системе. Заранее спасибо.

@Entity(name = "MainEntity")
@Table(name = "main_entity")
public class MainEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private Integer id;

  // Other @Columns defined here

  @ManyToOne()
  @JoinColumn(name = "entity_1_id")
  private Entity1 entity1;

  @ManyToOne()
  @JoinColumn(name = "entity_2_id")
  private Entity2 entity2;

  @ManyToOne()
  @JoinColumn(name = "entity_3_id")
  private Entity3 entity3;

  // ... and so on, for a total of 7 @ManyToOne() columns
}

Вот метод findAll(), который у меня есть:

final List<E> findAllOrdered(Class<E> clazz, Order order) {
    final Session session = sessionManager.openNewSession();
    try {
        return session.createCriteria(clazz)
                .addOrder(order)
                .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                .list();
    } finally {
        sessionManager.closeSession(session);
    }
}

Я обнаружил, что мне нужно добавить Criteria.DISTINCT_ROOT_ENTITY, потому что мы получали дублирующиеся результаты MainEntity, если у ребенка было несколько связанных с ним. Я подозреваю, что это большая часть моей проблемы с производительностью.

использовать кеш второго уровня Hibernate

Chol Nhial 16.03.2018 13:53

Вам не кажется, что все эти отношения ManyToOne влияют на производительность. Вроде их много. Кроме того, есть ли у них собственные отношения, которые нетерпеливо загружены?

Chol Nhial 16.03.2018 13:55

Сколько запросов запускает метод findAll? Он не использует никакого соединения? Почему вы думаете, что это идеальное узкое место? И не хотите ли загружать ассоциации ManyToOne?

Madhusudana Reddy Sunnapu 16.03.2018 13:55

@CholNhial - да, я точно думаю, что они вызывают проблему, я просто не знаю, как лучше с этим справиться. У некоторых из этих отношений есть другие, более ЖЕСТКИЕ. Я не думал об этом.

joshft91 16.03.2018 14:06

@MadhusudanaReddySunnapu - он много запускает, я включил отладку SQL через Hibernate, и, похоже, он выполняет тонну вызовов left outer join. Я думаю, что моя проблема, на что указал Чол, заключается в том, что некоторые из сопоставленных взаимосвязей также активно загружают данные.

joshft91 16.03.2018 14:08

@ joshft91 Я бы предпочел по умолчанию делать ассоциации Ленивыми. И присоединяйтесь к ним в HQL / критериях, основанных на том, какую ассоциацию мы действительно хотим получить. Также перед этим посмотрите количество запускаемых SQL-запросов, действительно ли они требуются, настроены ли запросы и т.д., и, соответственно, выберите LAZY или EAGER.

Madhusudana Reddy Sunnapu 16.03.2018 14:14

@MadhusudanaReddySunnapu - есть ли хороший способ узнать, сколько SQL-запросов запускается? В настоящее время я могу просто включить отладку SQL, где отображается сам запрос, но есть ли какие-либо другие советы по отладке Hibernate, о которых мне следует знать? Я действительно думаю, что мне, возможно, придется переопределить эту находку, чтобы я получал только ту информацию, которую хочу, от отношений.

joshft91 16.03.2018 14:20

Первым делом нужно включить show_sql, как в mkyong.com/hibernate/…. Это было бы хорошей отправной точкой, и впоследствии вы можете настроить свои ассоциации на LAZY / EAGER, SELECT / JOIN / SUBSELECT и настроить эти запросы, запустив их из какого-либо редактора БД, заметив стоимость / время для запроса.

Madhusudana Reddy Sunnapu 16.03.2018 14:30
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
8
537
2

Ответы 2

Если вы получаете нежелательный ответ и хотите отфильтровать, вы можете использовать @JsonIgnore

например:

  @ManyToOne()
  @JoinColumn(name = "entity_1_id")
  @JsonIgnore
  private Entity1 entity1;

Несколько советов для рассмотрения:

  1. Подумайте о том, чтобы сделать ассоциации ленивыми по умолчанию, если вы действительно не хотите загружать все данные ассоциации и их ассоциации вместе с родителем.

  2. Используйте JOIN в HQL / критериях в зависимости от того, какую ассоциацию мы действительно хотим получить, и глубины ассоциаций.

  3. Или используйте EntityGraph, чтобы решить, какие ассоциации нужно получить.

  4. Включите show_sql, так как это показывает количество SQL-запросов и точные SQL-запросы, которые запускаются в БД. Это было бы хорошей отправной точкой, и впоследствии вы можете настроить свои ассоциации на LAZY / EAGER, SELECT / JOIN / SUBSELECT в зависимости от вашего варианта использования.

  5. Вы можете запустить эти запросы к БД и посмотреть, поможет ли настройка запроса / БД (индексы, секционирование и т. д.) Сократить время запроса.

  6. Посмотрите, поможет ли кеш второго уровня в вашем случае использования. Обратите внимание, что кэш второго уровня будет иметь свою сложность и дополнительные накладные расходы, особенно если данные относятся к транзакционному типу, а не только для чтения. Когда приложение развернуто на узлах, еще одним аспектом, о котором стоит подумать, станет сохранение согласованности кеша. Необходимо проверить, действительно ли дополнительные накладные расходы и сложность оправдывают эффективность кеш-памяти второго уровня.

  7. С точки зрения дизайна приложения вы также можете подумать и посмотреть, действительно ли вы хотите получить MainEntity и ассоциации в одном запросе или пользовательском интерфейсе. Вместо этого мы могли бы сначала показать MainEntity с некоторым разбиением по страницам и на основе выбора мы могли бы получить ассоциации для этого MainEntity с помощью разбиения по страницам.

Обратите внимание, это не полный список. Но это хорошая отправная точка, и на основе вашего варианта использования вы можете увидеть, какой из них подойдет вам, а также любые другие дополнительные методы.

Другие вопросы по теме