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

Руководство по созданию и использованию метамодели критериев JPA

Узнайте, как создать метамодель JPA с помощью инструмента Hibernate jpamodelgen и как использовать его для написания типобезопасных запросов API критериев.

Автор оригинала: Vlad Mihalcea.

Вступление

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

Модель предметной области

Предположим, что ваше приложение использует следующие Сообщение , Комментарий к сообщению , Сведения о публикации и Налоговые сущности, которые образуют отношения “один ко многим”, “один ко многим” и “многие ко многим” таблицы :

Как создать метамодель критериев JPA

Инструмент hibernate-jpamodelgen , предоставляемый Hibernate ORM, может использоваться для сканирования объектов проекта и создания метамодели критериев JPA. Все, что вам нужно сделать, это добавить следующий путь к обработчику аннотаций в maven-компилятор-плагин в Maven pom.xml файл конфигурации:


    org.apache.maven.plugins
    maven-compiler-plugin
    ${maven-compiler-plugin.version}
    
        
            
                org.hibernate
                hibernate-jpamodelgen
                ${hibernate.version}
            
        
    

Теперь, когда проект скомпилирован, вы можете видеть, что в папке target создаются следующие классы Java:

> tree target/generated-sources/
target/generated-sources/
└── annotations
    └── com
        └── vladmihalcea
            └── book
                └── hpjp
                    └── hibernate
                        ├── forum
                        │   ├── PostComment_.java
                        │   ├── PostDetails_.java
                        │   ├── Post_.java
                        │   └── Tag_.java

Метамодель сущности тега

Если объект Tag отображается следующим образом:

@Entity
@Table(name = "tag")
public class Tag {

    @Id
    private Long id;

    private String name;

    //Getters and setters omitted for brevity
}

Класс Tag_ Метамодели создается следующим образом:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Tag.class)
public abstract class Tag_ {

    public static volatile SingularAttribute name;
    public static volatile SingularAttribute id;

    public static final String NAME = "name";
    public static final String ID = "id";
}

Атрибут SingularAttribute используется для базовых идентификатора и имени | Тега атрибутов сущности JPA.

Метамодель Post сущности

Объект Post отображается следующим образом:

@Entity
@Table(name = "post")
public class Post {

    @Id
    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,
        fetch = FetchType.LAZY
    )
    @LazyToOne(LazyToOneOption.NO_PROXY)
    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
}

Сущность Post имеет два основных атрибута, идентификатор и заголовок , коллекцию “один ко многим” комментарии , ассоциацию “один к одному” детали и коллекцию “многие ко многим” теги .

Класс Post_ Метамодели создается следующим образом:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Post.class)
public abstract class Post_ {

    public static volatile ListAttribute comments;
    public static volatile SingularAttribute details;
    public static volatile SingularAttribute id;
    public static volatile SingularAttribute title;
    public static volatile ListAttribute tags;

    public static final String COMMENTS = "comments";
    public static final String DETAILS = "details";
    public static final String ID = "id";
    public static final String TITLE = "title";
    public static final String TAGS = "tags";
}

Основные идентификатор и заголовок атрибуты, а также взаимно однозначные детали ассоциации представлены атрибутом SingularAttribute , в то время как комментарии и теги коллекции представлены атрибутом JPA ListAttribute .

Метамодель сущности Сведений о публикации

Объект Сведения о публикации отображается следующим образом:

@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(fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name = "id")
    private Post post;
    
    //Getters and setters omitted for brevity
}

Все атрибуты сущности будут представлены атрибутом JPA SingularAttribute в соответствующем классе Post Details_ Метамодели:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(PostDetails.class)
public abstract class PostDetails_ {

    public static volatile SingularAttribute post;
    public static volatile SingularAttribute createdBy;
    public static volatile SingularAttribute id;
    public static volatile SingularAttribute createdOn;

    public static final String POST = "post";
    public static final String CREATED_BY = "createdBy";
    public static final String ID = "id";
    public static final String CREATED_ON = "createdOn";
}

Метамодель сущности с комментарием к сообщению

Комментарий к сообщению отображается следующим образом:

@Entity
@Table(name = "post_comment")
public class PostComment {

    @Id
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;

    private String review;
    
    //Getters and setters omitted for brevity
}

И все атрибуты сущности представлены атрибутом JPA SingularAttribute в соответствующем классе Комментариев к сообщению/| Метамодели:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(PostComment.class)
public abstract class PostComment_ {

    public static volatile SingularAttribute post;
    public static volatile SingularAttribute review;
    public static volatile SingularAttribute id;

    public static final String POST = "post";
    public static final String REVIEW = "review";
    public static final String ID = "id";
}

Использование метамодели критериев JPA

Без метамодели JPA запрос API критериев, который должен извлекать Комментарий к сообщению сущности, отфильтрованные по их связанному Сообщению названию, будет выглядеть следующим образом:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery query = builder.createQuery(PostComment.class);
Root postComment = query.from(PostComment.class);

Join post = postComment.join("post");

query.where(
    builder.equal(
        post.get("title"),
        "High-Performance Java Persistence"
    )
);

List comments = entityManager
    .createQuery(query)
    .getResultList();

Обратите внимание, что мы использовали строковый литерал post при создании экземпляра Join , и мы использовали строковый литерал title при ссылке на Post | title .

Метамодель JPA позволяет нам избежать жесткого кодирования атрибутов сущностей, как показано в следующем примере:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery query = builder.createQuery(PostComment.class);
Root postComment = query.from(PostComment.class);

Join post = postComment.join(PostComment_.post);

query.where(
    builder.equal(
        post.get(Post_.title),
        "High-Performance Java Persistence"
    )
);

List comments = entityManager
    .createQuery(query)
    .getResultList();

Написание запросов API критериев JPA намного проще, если вы используете инструмент завершения кода, такой как Codota. Ознакомьтесь с этой статьей для получения более подробной информации о плагине Codota IDE.

Или, допустим, мы хотим получить ОСТАНОВКУ проекции при фильтрации Публикации заголовка и Сведений о публикации , созданных на атрибутах.

Мы можем использовать метамодель при создании атрибутов объединения, а также при создании псевдонимов столбцов проекции ОСТАНОВКИ или при ссылке на атрибуты сущности, которые нам нужно отфильтровать:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery query = builder.createQuery(Object[].class);

Root postComment = query.from(PostComment.class);
Join post = postComment.join(PostComment_.post);

query.multiselect(
    postComment.get(PostComment_.id).alias(PostComment_.ID),
    postComment.get(PostComment_.review).alias(PostComment_.REVIEW),
    post.get(Post_.title).alias(Post_.TITLE)
);

query.where(
    builder.and(
        builder.like(
            post.get(Post_.title),
            "%Java Persistence%"
        ),
        builder.equal(
            post.get(Post_.details).get(PostDetails_.CREATED_BY),
            "Vlad Mihalcea"
        )
    )
);

List comments = entityManager
    .createQuery(query)
    .unwrap(Query.class)
    .setResultTransformer(Transformers.aliasToBean(PostCommentSummary.class))
    .getResultList();

Круто, правда?

Вывод

Метамодель JPA очень полезна, так как позволяет нам создавать типобезопасные запросы API по критериям. Создание метамодели несложно, так как Hibernate предлагает для этой цели инструмент jpamodelgen .

Если мы переименуем атрибут сущности, нам больше не придется беспокоиться о том, что мы пропустили обновление запроса, потому что проект даже не будет компилироваться, если метамодель изменится и запросы будут ссылаться на старое представление сущности JPA.