เนื่องจากนี่เป็นคำถามที่พบบ่อยมากฉันจึงเขียนบทความนี้ซึ่งคำตอบนี้อ้างอิงจาก
สมมติว่าโปรแกรมของเราจะใช้ต่อไปPost
, PostComment
, PostDetails
และTag
หน่วยงานซึ่งรูปแบบหนึ่งต่อหลายคนหนึ่งต่อหนึ่งและอีกหลายต่อหลายความสัมพันธ์ของตาราง :
วิธีสร้าง Metamodel เกณฑ์ JPA
hibernate-jpamodelgen
เครื่องมือให้โดย Hibernate ออมสามารถนำมาใช้ในการสแกนหน่วยงานโครงการและสร้างเกณฑ์ JPA Metamodel ทั้งหมดที่คุณต้องทำคือการเพิ่มต่อไปนี้annotationProcessorPath
ไปmaven-compiler-plugin
ใน Maven 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>
ตอนนี้เมื่อคอมไพล์โปรเจ็กต์คุณจะเห็นว่าในtarget
โฟลเดอร์คลาส Java ต่อไปนี้ถูกสร้างขึ้น:
> tree target/generated-sources/
target/generated-sources/
└── annotations
└── com
└── vladmihalcea
└── book
└── hpjp
└── hibernate
├── forum
│ ├── PostComment_.java
│ ├── PostDetails_.java
│ ├── Post_.java
│ └── Tag_.java
Metamodel เอนทิตีแท็ก
หากTag
แมปเอนทิตีดังต่อไปนี้:
@Entity
@Table(name = "tag")
public class Tag {
@Id
private Long id;
private String name;
}
Tag_
ระดับ Metamodel ถูกสร้างขึ้นเช่นนี้
@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";
}
SingularAttribute
ใช้สำหรับขั้นพื้นฐานid
และname
Tag
คุณลักษณะนิติบุคคล JPA
โพสต์ Metamodel เอนทิตี
Post
นิติบุคคลถูกแมปเช่นนี้
@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<>();
}
Post
นิติบุคคลมีสองคุณลักษณะพื้นฐานid
และtitle
เป็นหนึ่งต่อหลายคนcomments
คอลเลกชันหนึ่งต่อหนึ่งdetails
สมาคมและหลายต่อหลายtags
คอลเลกชัน
Post_
ระดับ Metamodel ถูกสร้างขึ้นดังต่อไปนี้:
@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";
}
พื้นฐานid
และtitle
แอตทริบิวต์ตลอดจนการdetails
เชื่อมโยงแบบหนึ่งต่อหนึ่งจะแสดงโดยSingularAttribute
ในขณะที่comments
และtags
คอลเลกชันโดยมีตัวแทน ListAttribute
JPA
PostDetails entity Metamodel
PostDetails
นิติบุคคลถูกแมปเช่นนี้
@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;
}
แอตทริบิวต์เอนทิตีทั้งหมดจะแสดงโดย JPA SingularAttribute
ในPostDetails_
คลาส Metamodel ที่เกี่ยวข้อง:
@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";
}
PostComment entity Metamodel
PostComment
ถูกแมปดังต่อไปนี้:
@Entity
@Table(name = "post_comment")
public class PostComment {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
private String review;
}
และแอตทริบิวต์เอนทิตีทั้งหมดแสดงโดย JPA SingularAttribute
ในPostComments_
คลาส Metamodel ที่เกี่ยวข้อง:
@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";
}
การใช้ JPA Criteria Metamodel
หากไม่มี JPA Metamodel แบบสอบถาม Criteria API ที่ต้องการดึงข้อมูลPostComment
เอนทิตีที่กรองด้วยPost
ชื่อที่เกี่ยวข้องจะมีลักษณะดังนี้:
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();
สังเกตว่าเราใช้post
String literal เมื่อสร้างJoin
อินสแตนซ์และเราใช้title
String literal เมื่ออ้างถึงไฟล์Post
title
.
JPA Metamodel ช่วยให้เราสามารถหลีกเลี่ยงแอตทริบิวต์ของเอนทิตีที่เข้ารหัสยากดังตัวอย่างต่อไปนี้:
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();
การเขียนแบบสอบถาม JPA Criteria API นั้นง่ายกว่ามากหากคุณใช้เครื่องมือเติมโค้ดเช่น Codota อ่านบทความนี้เพื่อดูรายละเอียดเพิ่มเติมเกี่ยวกับปลั๊กอิน Codota IDE
หรือสมมติว่าเราต้องการดึงการฉาย DTOในขณะที่กรองPost
title
และPostDetails
createdOn
แอตทริบิวต์
เราสามารถใช้ Metamodel เมื่อสร้างแอตทริบิวต์ join เช่นเดียวกับเมื่อสร้างนามแฝงคอลัมน์การฉาย DTO หรือเมื่ออ้างอิงแอตทริบิวต์เอนทิตีที่เราต้องการกรอง:
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();
เจ๋งใช่มั้ย?