Рубрики
Без рубрики

Начальный выпуск оптимизатора сохраняемости Hy

Первый выпуск Hy persistence Optimizer выпущен, и он предоставляет вам инструмент, который может автоматически обнаруживать проблемы с производительностью JPA и гибернации.

Автор оригинала: 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 List comments = 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 , которые вы также можете использовать,

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