Poiché questa è una domanda molto comune, ho scritto questo articolo , su cui si basa questa risposta.
Supponiamo che la nostra applicazione utilizza la seguente Post, PostComment, PostDetails, e Taggli enti, che formano un uno-a-molti, uno-a-uno, e molti-a-molti rapporti della tabella :

Come generare il metamodello dei criteri JPA
Lo hibernate-jpamodelgenstrumento fornito da Hibernate ORM può essere utilizzato per scansionare le entità del progetto e generare il metamodello dei criteri JPA. Tutto quello che devi fare è aggiungere quanto segue annotationProcessorPathal file di configurazione di maven-compiler-pluginMaven pom.xml:
<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>
Ora, quando il progetto è compilato, puoi vedere che nella targetcartella vengono generate le seguenti classi Java:
> tree target/generated-sources/
target/generated-sources/
└── annotations
└── com
└── vladmihalcea
└── book
└── hpjp
└── hibernate
├── forum
│ ├── PostComment_.java
│ ├── PostDetails_.java
│ ├── Post_.java
│ └── Tag_.java
Tag entità Metamodel
Se l' Tagentità è mappata come segue:
@Entity
@Table(name = "tag")
public class Tag {
@Id
private Long id;
private String name;
//Getters and setters omitted for brevity
}
La Tag_classe Metamodel viene generata in questo modo:
@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";
}
Il SingularAttributeè utilizzato per la base idename Tag attributi JPA entità.
Post entity Metamodel
L' Postentità è mappata in questo modo:
@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à ha due attributi di base ide title, una commentsraccolta uno-a-molti , un'associazione uno-a-uno detailse un molti-a-moltitags raccolta .
La Post_classe Metamodel viene generata come segue:
@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";
}
Gli attributi di base ide title, così come l' detailsassociazione uno-a-uno , sono rappresentati da un SingularAttributementre le commentse le tagscollezioni sono rappresentate dall'APPListAttribute .
Entità PostDetails Metamodel
L' PostDetailsentità è mappata in questo modo:
@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
}
Tutti gli attributi dell'entità saranno rappresentati dall'APP SingularAttributenella PostDetails_classe Metamodel associata :
@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";
}
Entità PostComment Metamodel
Il PostCommentè mappato come segue:
@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
}
Inoltre, tutti gli attributi dell'entità sono rappresentati dall'APP SingularAttributenella PostComments_classe Metamodel associata :
@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";
}
Utilizzo del metamodello dei criteri JPA
Senza il metamodello JPA, una query API Criteria che deve recuperare le PostCommententità filtrate in base al Posttitolo associato sarebbe simile a questa:
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();
Si noti che abbiamo utilizzato il valore postletterale String durante la creazione Joindell'istanza e il valore titleletterale String quando si fa riferimento aPost title .
Il metamodello JPA ci consente di evitare attributi di entità hardcoded, come illustrato dal seguente esempio:
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();
Scrivere le query API dei criteri JPA è molto più semplice se si utilizza uno strumento di completamento del codice come Codota. Dai un'occhiata a questo articolo per maggiori dettagli sul plug-in IDE Codota.
Oppure, supponiamo di voler recuperare una proiezione DTO filtrando il Post titlee ilPostDetails createdOn attributi e.
Possiamo usare il Metamodel quando creiamo gli attributi di join, così come quando creiamo gli alias della colonna di proiezione DTO o quando facciamo riferimento agli attributi dell'entità che dobbiamo filtrare:
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();
Fantastico, vero?