Автор оригинала: 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 SingularAttributename; 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 Listcomments = 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 ListAttributecomments; 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 SingularAttributepost; 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 SingularAttributepost; 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(); CriteriaQueryquery = 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(); CriteriaQueryquery = 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
Круто, правда?
Вывод
Метамодель JPA очень полезна, так как позволяет нам создавать типобезопасные запросы API по критериям. Создание метамодели несложно, так как Hibernate предлагает для этой цели инструмент jpamodelgen
.
Если мы переименуем атрибут сущности, нам больше не придется беспокоиться о том, что мы пропустили обновление запроса, потому что проект даже не будет компилироваться, если метамодель изменится и запросы будут ссылаться на старое представление сущности JPA.