¿Cómo generar el metamodelo de la entidad JPA?

94

En el espíritu de la seguridad de tipos asociada con CriteriaQuery, JPA 2.0 también tiene una API para admitir la representación Metamodel de entidades.

¿Alguien está al tanto de una implementación completamente funcional de esta API (para generar el metamodelo en lugar de crear las clases de metamodelo manualmente)? Sería increíble si alguien también conociera los pasos para configurar esto en Eclipse (supongo que es tan simple como configurar un procesador de anotaciones, pero nunca se sabe).

EDITAR: Acabo de tropezar con Hibernate JPA 2 Metamodel Generator . Pero el problema persiste ya que no puedo encontrar ningún enlace de descarga para el jar.

EDICIÓN 2: Ha pasado un tiempo desde que hice esta pregunta, pero pensé en volver y agregar un enlace al proyecto Hibernate JPA Model Generator en SourceForge

Andrey
fuente

Respuestas:

87

Sería increíble si alguien también conociera los pasos para configurar esto en Eclipse (supongo que es tan simple como configurar un procesador de anotaciones, pero nunca se sabe)

Sí lo es. Aquí están las implementaciones e instrucciones para las diversas implementaciones de JPA 2.0:

EclipseLink

Hibernar

OpenJPA

DataNucleus


La última implementación de Hibernate está disponible en:

Una implementación anterior de Hibernate está en:

Pascal Thivent
fuente
1
El enlace DataNucleus está muerto.
Karl Richter
1
El enlace de Hibernate también está muerto
Freelancer
43

Por favor, eche un vistazo a jpa-metamodels-with-maven-example .

Hibernar

  • Necesitamos org.hibernate.org:hibernate-jpamodelgen.
  • La clase de procesador es org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.

Hibernar como dependencia

    <dependency>
      <groupId>org.hibernate.orm</groupId>
      <artifactId>hibernate-jpamodelgen</artifactId>
      <version>${version.hibernate-jpamodelgen}</version>
      <scope>provided</scope>
    </dependency>

Hibernar como procesador

      <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <compilerArguments>-AaddGeneratedAnnotation=false</compilerArguments> <!-- suppress java.annotation -->
              <processors>
                <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
              </processors>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>${version.hibernate-jpamodelgen}</version>
          </dependency>
        </dependencies>
      </plugin>

OpenJPA

  • Necesitamos org.apache.openjpa:openjpa.
  • La clase de procesador es org.apache.openjpa.persistence.meta.AnnotationProcessor6.
  • OpenJPA parece requerir un elemento adicional <openjpa.metamodel>true<openjpa.metamodel>.

OpenJPA como dependencia

  <dependencies>
    <dependency>
      <groupId>org.apache.openjpa</groupId>
      <artifactId>openjpa</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <compilerArgs>
            <arg>-Aopenjpa.metamodel=true</arg>
          </compilerArgs>
        </configuration>
      </plugin>
    </plugins>
  </build>

OpenJPA como procesador

      <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <executions>
          <execution>
            <id>process</id>
            <goals>
              <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <processors>
                <processor>org.apache.openjpa.persistence.meta.AnnotationProcessor6</processor>
              </processors>
              <optionMap>
                <openjpa.metamodel>true</openjpa.metamodel>
              </optionMap>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
            <version>${version.openjpa}</version>
          </dependency>
        </dependencies>
      </plugin>

EclipseLink

  • Necesitamos org.eclipse.persistence:org.eclipse.persistence.jpa.modelgen.processor.
  • La clase de procesador es org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor.
  • EclipseLink requiere persistence.xml.

EclipseLink como dependencia

  <dependencies>
    <dependency>
      <groupId>org.eclipse.persistence</groupId>
      <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
      <scope>provided</scope>
    </dependency>

EclipseLink como procesador

    <plugins>
      <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <processors>
                <processor>org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor</processor>
              </processors>
              <compilerArguments>-Aeclipselink.persistencexml=src/main/resources-${environment.id}/META-INF/persistence.xml</compilerArguments>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
            <version>${version.eclipselink}</version>
          </dependency>
        </dependencies>
      </plugin>

DataNucleus

  • Necesitamos org.datanucleus:datanucleus-jpa-query.
  • La clase de procesador es org.datanucleus.jpa.query.JPACriteriaProcessor.

DataNucleus como dependencia

  <dependencies>
    <dependency>
      <groupId>org.datanucleus</groupId>
      <artifactId>datanucleus-jpa-query</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>

DataNucleus como procesador

      <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <executions>
          <execution>
            <id>process</id>
            <goals>
              <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <processors>
                <processor>org.datanucleus.jpa.query.JPACriteriaProcessor</processor>
              </processors>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-jpa-query</artifactId>
            <version>${version.datanucleus}</version>
          </dependency>
        </dependencies>
      </plugin>
Jin Kwon
fuente
3
Para ser claros, las cosas generadas se pueden usar con eclipselink, aunque usas hibernate para generarlas, no pude generar un metamodelo de netbeans 8 y tuve que crear un proyecto de prueba maven para generar mis cosas
Kalpesh Soni
@ymajoros ¿Está prohibido, en SO, decir something is recommendedsin IMHO? No estoy representando en nombre de nadie más.
Jin Kwon
1
Por cierto, consulte la respuesta de Sorter para EclipseLink. Esta es la configuración que he estado usando durante años y funciona perfectamente. stackoverflow.com/questions/3037593/…
ymajoros
¿No es esta implementación específica? Intento usar el metamodelo generado por Hibernate con EclipseLink y obtengo NullPointerException
Michał Ziobro
@ymajoros Aún necesita una persistence.xml, ¿no?
Jin Kwon
20

El soporte JPA 2.0 de Eclipse a través de Dali (que se incluye en "Eclipse IDE para desarrolladores JEE") tiene su propio generador de metamodelos integrado con Eclipse.

  1. Seleccione su proyecto en el Explorador de paquetes
  2. Vaya a Propiedades -> cuadro de diálogo JPA
  3. Seleccionar la carpeta fuente de Canonical metamodelo (2,0 JPA) grupo
  4. Haga clic en el botón Aplicar para generar clases de metamodelo en la carpeta de origen seleccionada

ingrese la descripción de la imagen aquí

Esto debería funcionar en cualquier proveedor de JPA ya que las clases generadas son estándar.

Vea también aquí .

James
fuente
¿Hay alguna forma de iniciar el proceso usted mismo? Esto no produce el metamodelo de manera confiable para mí
eso es
6

Para eclipselink, solo la siguiente dependencia es suficiente para generar metamodelo. No se necesita nada más.

    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.5.1</version>
        <scope>provided</scope>
    </dependency>
Clasificador
fuente
@Barthelomeus tu nota es falsa . EclipseLink 2.5.1+ generará clases de metamodelo para entidades no listadas también, solo especifique <exclude-unlisted-classes>false</exclude-unlisted-classes>en persisetence.xml
Michele Mariotti
Tenga en cuenta que eclipselink no se generará sinpersistence.xml
Jin Kwon
5

Para Hibernate como proveedor, que es el más común en mi humilde opinión:

En el caso de herramientas de compilación como Gradle, Maven, debe tener el jar del Generador de metamodelos JPA 2 de Hibernate en la ruta de clase y el nivel del compilador> = 1.6, eso es todo lo que necesita para compilar el proyecto y el metamodelo se generará automáticamente.

En el caso de IDE Eclipse 1. vaya a Proyecto-> Propiedades-> Compilador Java-> Procesamiento de anotaciones y habilítelo. 2. Expanda Procesamiento de anotaciones-> Ruta de fábrica-> Agregar jar externo agregue jarra del generador de metamodelo JPA 2 de Hibernate, verifique la jarra recién agregada y diga Aceptar. ¡Limpio y construido!

Enlace Hibernate JPA 2 Metamodel Generator enlace jar desde maven repo https://mvnrepository.com/artifact/org.hibernate/hibernate-jpamodelgen

SandeepGodara
fuente
En mi caso, agregar <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <scope>compile</scope> </dependency> </dependencies>a pom.xml fue suficiente.
Lu55
¿Necesito ambas configuraciones cuando uso maven y Eclipse?
Melkor
a pesar de que se agregó hibernate-jpamodelgen en el pom, tuve que hacer esto y funcionó
Freelancer
3

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 :

Metamodelo de criterios JPA

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?

Vlad Mihalcea
fuente
0

Ok, según lo que he leído aquí, lo hice con EclipseLink de esta manera y no necesitaba poner la dependencia del procesador al proyecto, solo como un annotationProcessorPathelemento del complemento del compilador.

    <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <annotationProcessorPaths>
                <annotationProcessorPath>
                    <groupId>org.eclipse.persistence</groupId>
                    <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
                    <version>2.7.7</version>
                </annotationProcessorPath>
            </annotationProcessorPaths>
            <compilerArgs>
                <arg>-Aeclipselink.persistencexml=src/main/resources/META-INF/persistence.xml</arg>
            </compilerArgs>
        </configuration>
    </plugin>
dmatej
fuente