Comme il s'agit d'une question très courante, j'ai écrit cet article , sur lequel cette réponse est basée.
Supposons que notre application utilise les éléments suivants Post, PostComment, PostDetailset des Tagentités qui forment un à plusieurs, un à un, et plusieurs à plusieurs relations de table :

Comment générer le métamodèle de critères JPA
L' hibernate-jpamodelgenoutil fourni par Hibernate ORM peut être utilisé pour analyser les entités du projet et générer le métamodèle de critères JPA. Tout ce que vous devez faire est d' ajouter ce qui suit annotationProcessorPathà maven-compiler-pluginla Maven pom.xmlfichier de configuration:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>${hibernate.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</plugin>
Maintenant, lorsque le projet est compilé, vous pouvez voir que dans le targetdossier, les classes Java suivantes sont générées:
> tree target/generated-sources/
target/generated-sources/
└── annotations
└── com
└── vladmihalcea
└── book
└── hpjp
└── hibernate
├── forum
│ ├── PostComment_.java
│ ├── PostDetails_.java
│ ├── Post_.java
│ └── Tag_.java
Métamodèle d'entité de balise
Si l' Tagentité est mappée comme suit:
@Entity
@Table(name = "tag")
public class Tag {
@Id
private Long id;
private String name;
//Getters and setters omitted for brevity
}
La Tag_classe Metamodel est générée comme ceci:
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Tag.class)
public abstract class Tag_ {
public static volatile SingularAttribute<Tag, String> name;
public static volatile SingularAttribute<Tag, Long> id;
public static final String NAME = "name";
public static final String ID = "id";
}
Le SingularAttributeest utilisé pour les attributs d'entité de base idet name TagJPA.
Métamodèle d'entité de poste
L' Postentité est mappée comme ceci:
@Entity
@Table(name = "post")
public class Post {
@Id
private Long id;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> 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<Tag> tags = new ArrayList<>();
//Getters and setters omitted for brevity
}
L' Postentité possède deux attributs de base, idet titleune collection un-à-plusieurs comments, une association un-à-un detailset une collection plusieurs-à-plusieurs tags.
La Post_classe Metamodel est générée comme suit:
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Post.class)
public abstract class Post_ {
public static volatile ListAttribute<Post, PostComment> comments;
public static volatile SingularAttribute<Post, PostDetails> details;
public static volatile SingularAttribute<Post, Long> id;
public static volatile SingularAttribute<Post, String> title;
public static volatile ListAttribute<Post, Tag> 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";
}
Les attributs de base idet title, ainsi que l'association un-à-un details, sont représentés par un SingularAttributetandis que les collections commentset tagssont représentées par le JPA ListAttribute.
Métamodèle d'entité PostDetails
L' PostDetailsentité est mappée comme ceci:
@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
}
Tous les attributs d'entité vont être représentés par le JPA SingularAttributedans la PostDetails_classe Metamodel associée :
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(PostDetails.class)
public abstract class PostDetails_ {
public static volatile SingularAttribute<PostDetails, Post> post;
public static volatile SingularAttribute<PostDetails, String> createdBy;
public static volatile SingularAttribute<PostDetails, Long> id;
public static volatile SingularAttribute<PostDetails, Date> 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";
}
Métamodèle d'entité PostComment
Le PostCommentest mappé comme suit:
@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
}
Et, tous les attributs d'entité sont représentés par le JPA SingularAttributedans la PostComments_classe Metamodel associée :
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(PostComment.class)
public abstract class PostComment_ {
public static volatile SingularAttribute<PostComment, Post> post;
public static volatile SingularAttribute<PostComment, String> review;
public static volatile SingularAttribute<PostComment, Long> id;
public static final String POST = "post";
public static final String REVIEW = "review";
public static final String ID = "id";
}
Utilisation du métamodèle de critères JPA
Sans le métamodèle JPA, une requête d'API Criteria qui a besoin de récupérer les PostCommententités filtrées par leur Posttitre associé ressemblerait à ceci:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<PostComment> query = builder.createQuery(PostComment.class);
Root<PostComment> postComment = query.from(PostComment.class);
Join<PostComment, Post> post = postComment.join("post");
query.where(
builder.equal(
post.get("title"),
"High-Performance Java Persistence"
)
);
List<PostComment> comments = entityManager
.createQuery(query)
.getResultList();
Notez que nous avons utilisé le postlittéral String lors de la création de l' Joininstance et que nous avons utilisé le titlelittéral String lors du référencement du Post title.
Le métamodèle JPA nous permet d'éviter le codage en dur des attributs d'entité, comme illustré par l'exemple suivant:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<PostComment> query = builder.createQuery(PostComment.class);
Root<PostComment> postComment = query.from(PostComment.class);
Join<PostComment, Post> post = postComment.join(PostComment_.post);
query.where(
builder.equal(
post.get(Post_.title),
"High-Performance Java Persistence"
)
);
List<PostComment> comments = entityManager
.createQuery(query)
.getResultList();
L'écriture de requêtes API JPA Criteria est beaucoup plus facile si vous utilisez un outil de complétion de code comme Codota. Consultez cet article pour plus de détails sur le plugin Codota IDE.
Ou, disons que nous voulons récupérer une projection DTO tout en filtrant Post titleles PostDetails createdOnattributs et.
Nous pouvons utiliser le métamodèle lors de la création des attributs de jointure, ainsi que lors de la construction des alias de colonne de projection DTO ou lors du référencement des attributs d'entité que nous devons filtrer:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = builder.createQuery(Object[].class);
Root<PostComment> postComment = query.from(PostComment.class);
Join<PostComment, Post> 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<PostCommentSummary> comments = entityManager
.createQuery(query)
.unwrap(Query.class)
.setResultTransformer(Transformers.aliasToBean(PostCommentSummary.class))
.getResultList();
Cool, non?