Ponieważ jest to bardzo częste pytanie, napisałem ten artykuł , na którym opiera się ta odpowiedź.
Załóżmy, że nasza aplikacja wykorzystuje następujące Post, PostComment, PostDetailsoraz Tagpodmioty, które tworzą jeden-do-wielu, jeden-na-jeden, a wiele do wielu relacji tabeli :

Jak wygenerować metamodel kryteriów JPA
hibernate-jpamodelgenNarzędzie dostarczane przez Hibernate ORM może być używany do skanowania jednostek projektowych i generowanie kryteria WZP metamodel. Wszystko, co musisz zrobić, to dodać następujące elementy annotationProcessorPathdo pliku konfiguracyjnego maven-compiler-pluginMavena 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>
Teraz, gdy projekt jest kompilowany, możesz zobaczyć, że w targetfolderze generowane są następujące klasy Java:
> tree target/generated-sources/
target/generated-sources/
└── annotations
└── com
└── vladmihalcea
└── book
└── hpjp
└── hibernate
├── forum
│ ├── PostComment_.java
│ ├── PostDetails_.java
│ ├── Post_.java
│ └── Tag_.java
Oznacz encję Metamodel
Jeśli Tagjednostka jest mapowana w następujący sposób:
@Entity
@Table(name = "tag")
public class Tag {
@Id
private Long id;
private String name;
}
Tag_Klasa Metamodel generowany jest tak:
@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";
}
SingularAttributeSłuży do podstawowych idi name TagWZP atrybutów jednostki.
Opublikuj Metamodel encji
PostJednostka jest odwzorowany tak:
@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<>();
}
Plik PostJednostka ma dwa podstawowe atrybuty, idi title, jeden-do-wielu commentskolekcji, jeden-do-jednego detailsstowarzyszenia oraz wiele-do-wielu tagskolekcji.
Post_Klasa Metamodel generowany jest w następujący sposób:
@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";
}
Podstawowe idi titleatrybuty, a także detailspowiązanie jeden do jednego , są reprezentowane przezSingularAttribute chwilę, commentsa tagskolekcje i są reprezentowane przez WZP ListAttribute.
Encja PostDetails Metamodel
Plik PostDetailsJednostka jest odwzorowany tak:
@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;
}
Wszystkie atrybuty encji będą reprezentowane przez JPA SingularAttributew powiązanymPostDetails_ klasie 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";
}
Jednostka PostComment Metamodel
PostCommentSą odwzorowywane w sposób następujący:
@Entity
@Table(name = "post_comment")
public class PostComment {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
private String review;
}
Wszystkie atrybuty encji są reprezentowane przez JPA SingularAttributew powiązanej PostComments_klasie 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";
}
Korzystanie z metamodelu kryteriów JPA
Bez metamodelu JPA zapytanie Criteria API, które musi pobrać PostCommentjednostki przefiltrowane według powiązanego Posttytułu, wyglądałoby tak:
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();
Zauważ, że użyliśmy postliterału String podczas tworzenia Joinwystąpienia i użyliśmy titleliterału String podczas odwoływania się doPost title .
Metamodel JPA pozwala nam uniknąć zakodowanych na stałe atrybutów encji, co ilustruje poniższy przykład:
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();
Pisanie zapytań JPA Criteria API jest znacznie łatwiejsze, jeśli używasz narzędzia do uzupełniania kodu, takiego jak Codota. Przeczytaj ten artykuł, aby uzyskać więcej informacji na temat wtyczki Codota IDE.
Lub, powiedzmy, że chcemy pobrać projekcję DTO podczas filtrowania plikówPost title iPostDetails createdOn atrybuty.
Możemy użyć Metamodelu podczas tworzenia atrybutów złączenia, a także podczas budowania aliasów kolumn projekcji DTO lub podczas odwoływania się do atrybutów encji, które musimy filtrować:
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();
Fajnie, prawda?