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 Tag
entitas, yang membentuk satu-ke-banyak, satu-ke-satu, dan banyak-ke-banyak hubungan tabel :
Cara membuat Metamodel Kriteria JPA
The hibernate-jpamodelgen
alat 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 annotationProcessorPath
ke maven-compiler-plugin
dalam pom.xml
file 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 target
folder, 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 Tag
entitas 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 SingularAttribute
digunakan untuk dasar id
dan name
Tag
atribut entitas JPA.
Posting entitas Metamodel
The Post
entitas 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 Post
entitas memiliki dua atribut dasar, id
dan title
, satu-ke-banyak comments
koleksi, satu-ke-satu details
asosiasi, dan banyak-ke-banyak tags
koleksi.
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 id
dan title
, serta details
asosiasi satu-ke-satu , diwakili oleh SingularAttribute
sementara koleksi comments
dan tags
diwakili oleh JPA ListAttribute
.
Entitas PostDetails Metamodel
The PostDetails
entitas 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 SingularAttribute
di 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 PostComment
dipetakan 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 SingularAttribute
di 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 PostComment
entitas yang difilter menurut Post
judul 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 post
literal String saat membuat Join
instance, dan kami menggunakan title
literal 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
title
dan PostDetails
createdOn
atribut.
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?