Автор оригинала: Vlad Mihalcea.
Вступление
В конце 2018 года у меня появилась идея написать инструмент, который может автоматически обнаруживать проблемы с JPA и гибернацией, сканируя уровень доступа к данным и предоставляя вам советы по оптимизации.
В начале февраля Теодор Чайкалис удивил меня этим комментарием в Facebook, который укрепил идею о том, что наличие такого инструмента было бы действительно потрясающим для разработчиков Java, работающих с JPA и Hibernate.
В конце февраля у меня появилось немного свободного времени, и я начал работать над этим, и реакция в социальных сетях превзошла мои ожидания:
Представьте, что у вас есть инструмент, который может автоматически определять, правильно ли вы используете @Java Сохраняемость и #Спящий режим . Больше никаких проблем с производительностью, больше никаких глупых ошибок, которые могут стоить вам много времени и денег. Скоро вы сможете получить этот инструмент. Оставайтесь с нами для получения более подробной информации! pic.twitter.com/CRYx4tVPif
Сегодня я рад сообщить вам, что первоначальная версия готова.
Оптимизатор настойчивости Hy наконец-то прибыл!
Время тестирования
Предположим, что ваше приложение определяет четыре сущности JPA: Сообщение
, Сведения о публикации
, Комментарий к публикации
и Тег
, которые связаны следующим образом:
Сопоставление объектов JPA
Объект Post
отображается следующим образом:
@Entity @Table(name = "post") public class Post { @Id @GeneratedValue private Long id; private String title; @OneToMany( mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true ) private Listcomments = new ArrayList<>(); @OneToOne( mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY ) private PostDetails details; @ManyToMany @JoinTable( name = "post_tag", joinColumns = @JoinColumn(name = "post_id"), inverseJoinColumns = @JoinColumn(name = "tag_id") ) private List tags = new ArrayList<>(); //Getters and setters omitted for brevity public void addComment(PostComment comment) { comments.add(comment); comment.setPost(this); } public void removeComment(PostComment comment) { comments.remove(comment); comment.setPost(null); } public void addDetails(PostDetails details) { this.details = details; details.setPost(this); } public void removeDetails() { this.details.setPost(null); this.details = null; } }
Сведения о публикации
имеет взаимно однозначную связь с родительской Публикацией
сущностью и отображается следующим образом:
@Entity @Table(name = "post_details") public class PostDetails { @Id @GeneratedValue private Long id; @Column(name = "created_on") private Date createdOn; @Column(name = "created_by") private String createdBy; @OneToOne private Post post; //Getters and setters omitted for brevity }
Комментарий Post
имеет отношение “многие к одному” с родительской Публикацией
сущностью и отображается, как показано в следующем фрагменте кода:
@Entity @Table(name = "post_comment") public class PostComment { @Id @GeneratedValue private Long id; @ManyToOne private Post post; private String review; //Getters and setters omitted for brevity }
Объект Tag
отображается следующим образом:
@Entity @Table(name = "tag") public class Tag { @Id @GeneratedValue private Long id; private String name; //Getters and setters omitted for brevity }
Конфигурация оптимизатора сохраняемости Hy
Теперь давайте создадим экземпляр Оптимизатор сохраняемости Hy
объект, передавая ему текущий режим гибернации SessionFactory
и вызывая метод init
:
new HypersistenceOptimizer( new HibernateConfig( sessionFactory() ) );
При проверке журнала приложений мы можем увидеть следующие советы по оптимизации:
ERROR [main]: Hypersistence Optimizer - CRITICAL - EagerFetchingEvent - The [post] attribute in the [io.hypersistence.optimizer.hibernate.mapping.association.PostDetails] entity uses eager fetching. Consider using a lazy fetching which, not only that is more efficient, but it is way more flexible when it comes to fetching data. ERROR [main]: Hypersistence Optimizer - CRITICAL - OneToOneWithoutMapsIdEvent - The [post] one-to-one association in the [io.hypersistence.optimizer.hibernate.mapping.association.PostDetails] entity is using a separate Foreign Key to reference the parent record. Consider using @MapsId so that the identifier is shared with the parent row. ERROR [main]: Hypersistence Optimizer - CRITICAL - EagerFetchingEvent - The [post] attribute in the [io.hypersistence.optimizer.hibernate.mapping.association.PostComment] entity uses eager fetching. Consider using a lazy fetching which, not only that is more efficient, but it is way more flexible when it comes to fetching data. ERROR [main]: Hypersistence Optimizer - CRITICAL - ManyToManyListEvent - The [tags] many-to-many association in the [io.hypersistence.optimizer.hibernate.mapping.association.Post] entity is using a List so it does not render very efficient SQL statements. Consider using a Set instead. ERROR [main]: Hypersistence Optimizer - CRITICAL - OneToOneParentSideEvent - The [details] one-to-one association in the [io.hypersistence.optimizer.hibernate.mapping.association.Post] entity is mapped as the parent-side of this relationship. The parent-side is fetched eagerly unless bytecode enhancement lazy loading is employed, and even then, there are limitations to how the one-to-one association can be mapped. You should consider mapping the child-side only with @MapsId so that you can always reference the parent entity while having a reference to the child.
Как это круто, правда?
Теперь давайте рассмотрим эти советы по оптимизации:
- Как я объяснил в этой статье , НЕТЕРПЕЛИВАЯ выборка очень плохо влияет на производительность , так как это может привести к N+1 проблемам с запросами , и стратегия НЕТЕРПЕЛИВОЙ выборки не может быть переопределена на основе каждого запроса.
- Родительская сторона ассоциации
@OneToOne
также извлекается с нетерпением, если не используется усовершенствование байт-кода. Ознакомьтесь с этой статьей для получения более подробной информации. - Только в том случае, если мы используем
@MapsId
на стороне клиента ассоциации@OneToOne
, ассоциация JPA сопоставит реальные отношения таблицы “один к одному”. В противном случае мы получим отношение таблицы “один ко многим”, в котором внешний ключ уникален. - Ассоциация
@ManyToMany
лучше работает с коллекциямиSet
. ИспользованиеСписка
создаст больше запросов, чем необходимо. Для получения более подробной информации ознакомьтесь с этой статьей .
Оставайтесь с нами для получения дополнительной информации!
Если вам интересно, сколько проблем возникает в вашем текущем проекте, вы можете загрузить и установить пробную версию .
Также есть множество примеров в репозитории hypertensive-optimizer
GitHub , которые вы также можете использовать,
Так вот, это только первый релиз. В следующих выпусках будет добавлена поддержка конфигурации гибернации, поэтому следите за обновлениями для более впечатляющей оптимизации производительности.