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
, PostDetails
et des Tag
entité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-jpamodelgen
outil 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-plugin
la Maven pom.xml
fichier 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 target
dossier, 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' Tag
entité 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 SingularAttribute
est utilisé pour les attributs d'entité de base id
et name
Tag
JPA.
Métamodèle d'entité de poste
L' Post
entité 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' Post
entité possède deux attributs de base, id
et title
une collection un-à-plusieurs comments
, une association un-à-un details
et 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 id
et title
, ainsi que l'association un-à-un details
, sont représentés par un SingularAttribute
tandis que les collections comments
et tags
sont représentées par le JPA ListAttribute
.
Métamodèle d'entité PostDetails
L' PostDetails
entité 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 SingularAttribute
dans 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 PostComment
est 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 SingularAttribute
dans 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 PostComment
entités filtrées par leur Post
titre 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 post
littéral String lors de la création de l' Join
instance et que nous avons utilisé le title
litté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
title
les PostDetails
createdOn
attributs 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?