Автор оригинала: 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(); 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.