Dado que esta es una pregunta muy común, escribí este artículo , en el que se basa esta respuesta.
Asumamos nuestra aplicación utiliza la siguiente Post, PostComment, PostDetails, y Tagentidades, que forman un uno-a-muchos, uno-a-uno, y muchos-a-muchos relaciones de la tabla :

Cómo generar el metamodelo de criterios JPA
La hibernate-jpamodelgenherramienta proporcionada por Hibernate ORM se puede utilizar para escanear las entidades del proyecto y generar el metamodelo de criterios JPA. Todo lo que necesita hacer es agregar lo siguiente annotationProcessorPathal archivo de configuración de 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>
Ahora, cuando se compila el proyecto, puede ver que en la targetcarpeta, se generan las siguientes clases de Java:
> tree target/generated-sources/
target/generated-sources/
└── annotations
└── com
└── vladmihalcea
└── book
└── hpjp
└── hibernate
├── forum
│ ├── PostComment_.java
│ ├── PostDetails_.java
│ ├── Post_.java
│ └── Tag_.java
Metamodelo de entidad de etiqueta
Si la Tagentidad se asigna de la siguiente manera:
@Entity
@Table(name = "tag")
public class Tag {
@Id
private Long id;
private String name;
//Getters and setters omitted for brevity
}
La Tag_clase Metamodel se genera así:
@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";
}
Se SingularAttributeutiliza para los atributos de entidad básicos idy name TagJPA.
Publicar metamodelo de entidad
La Postentidad se asigna así:
@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
}
La Postentidad tiene dos atributos básicos idy titleuna commentscolección de uno a muchos , una detailsasociación de uno a uno y una tagscolección de muchos a muchos .
La Post_clase Metamodel se genera de la siguiente manera:
@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";
}
Los atributos básicos idy title, así como la detailsasociación uno a uno , están representados por un SingularAttributetiempo, las colecciones commentsy tagsestán representadas por el JPA ListAttribute.
Metamodelo de entidad PostDetails
La PostDetailsentidad se asigna así:
@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
}
Todos los atributos de la entidad serán representados por el JPA SingularAttributeen la PostDetails_clase Metamodel asociada :
@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";
}
Entidad PostComment Metamodel
El PostCommentse asigna de la siguiente manera:
@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
}
Y todos los atributos de la entidad están representados por el JPA SingularAttributeen la PostComments_clase Metamodel asociada :
@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";
}
Uso del metamodelo de criterios JPA
Sin el metamodelo JPA, una consulta de la API de criterios que necesita buscar las PostCommententidades filtradas por su Posttítulo asociado se vería así:
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();
Observe que usamos el postliteral String al crear la Joininstancia, y usamos el titleliteral String al hacer referencia al Post title.
El metamodelo JPA nos permite evitar atributos de entidad de codificación rígida, como se ilustra en el siguiente ejemplo:
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();
Escribir consultas de API de criterios de JPA es mucho más fácil si está utilizando una herramienta de finalización de código como Codota. Consulte este artículo para obtener más detalles sobre el complemento IDE de Codota.
O digamos que queremos obtener una proyección DTO mientras filtramos Post titlelos PostDetails createdOnatributos y.
Podemos usar el Metamodelo al crear los atributos de unión, así como al construir los alias de la columna de proyección DTO o al hacer referencia a los atributos de entidad que necesitamos filtrar:
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();
¿Guay, verdad?