Karena ini adalah pertanyaan yang sangat umum, saya menulis artikel ini , yang menjadi dasar jawaban ini.
Mari kita asumsikan aplikasi kita menggunakan berikut Post, PostComment, PostDetails, dan Tagentitas, yang membentuk satu-ke-banyak, satu-ke-satu, dan banyak-ke-banyak hubungan tabel :

Cara membuat Metamodel Kriteria JPA
The hibernate-jpamodelgenalat yang disediakan oleh Hibernate ORM dapat digunakan untuk memindai entitas proyek dan menghasilkan Kriteria JPA Metamodel. Yang perlu Anda lakukan adalah menambahkan yang berikut ini annotationProcessorPathke maven-compiler-plugindalam pom.xmlfile konfigurasi Maven :
<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>
Sekarang, saat proyek dikompilasi, Anda dapat melihat bahwa di targetfolder, kelas Java berikut dibuat:
> tree target/generated-sources/
target/generated-sources/
└── annotations
└── com
└── vladmihalcea
└── book
└── hpjp
└── hibernate
├── forum
│ ├── PostComment_.java
│ ├── PostDetails_.java
│ ├── Post_.java
│ └── Tag_.java
Metamodel entitas tag
Jika Tagentitas dipetakan sebagai berikut:
@Entity
@Table(name = "tag")
public class Tag {
@Id
private Long id;
private String name;
//Getters and setters omitted for brevity
}
Kelas Tag_Metamodel dibuat seperti ini:
@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";
}
The SingularAttributedigunakan untuk dasar iddan name Tagatribut entitas JPA.
Posting entitas Metamodel
The Postentitas dipetakan seperti ini:
@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
}
The Postentitas memiliki dua atribut dasar, iddan title, satu-ke-banyak commentskoleksi, satu-ke-satu detailsasosiasi, dan banyak-ke-banyak tagskoleksi.
Kelas Post_Metamodel dibuat sebagai berikut:
@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";
}
Atribut dasar iddan title, serta detailsasosiasi satu-ke-satu , diwakili oleh SingularAttributesementara koleksi commentsdan tagsdiwakili oleh JPA ListAttribute.
Entitas PostDetails Metamodel
The PostDetailsentitas dipetakan seperti ini:
@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
}
Semua atribut entitas akan diwakili oleh JPA SingularAttributedi PostDetails_kelas Metamodel terkait :
@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";
}
Metamodel entitas PostComment
The PostCommentdipetakan sebagai berikut:
@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
}
Dan, semua atribut entitas diwakili oleh JPA SingularAttributedi PostComments_kelas Metamodel terkait :
@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";
}
Menggunakan Metamodel Kriteria JPA
Tanpa Metamodel JPA, kueri API Kriteria yang perlu mengambil PostCommententitas yang difilter menurut Postjudul terkait akan terlihat seperti ini:
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();
Perhatikan bahwa kami menggunakan postliteral String saat membuat Joininstance, dan kami menggunakan titleliteral String saat mereferensikan file Post title.
Metamodel JPA memungkinkan kita untuk menghindari atribut entitas pengkodean keras, seperti yang diilustrasikan oleh contoh berikut:
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();
Menulis kueri JPA Criteria API jauh lebih mudah jika Anda menggunakan alat penyelesaian kode seperti Codota. Lihat artikel ini untuk detail lebih lanjut tentang plugin Codota IDE.
Atau, katakanlah kita ingin mengambil sebuah proyeksi DTO sementara menyaring Post titledan PostDetails createdOnatribut.
Kita dapat menggunakan Metamodel saat membuat atribut gabungan, serta saat membangun alias kolom proyeksi DTO atau saat mereferensikan atribut entitas yang perlu kita filter:
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();
Keren kan?