Comment générer le métamodèle d'entité JPA?

94

Dans l'esprit de la sécurité de type associée à CriteriaQuery, JPA 2.0 dispose également d'une API pour prendre en charge la représentation métamodèle des entités.

Quelqu'un est-il au courant d'une implémentation entièrement fonctionnelle de cette API (pour générer le métamodèle au lieu de créer les classes de métamodèle manuellement)? Ce serait génial si quelqu'un connaît également les étapes de configuration dans Eclipse (je suppose que c'est aussi simple que de configurer un processeur d'annotations, mais on ne sait jamais).

EDIT: Je viens de tomber sur Hibernate JPA 2 Metamodel Generator . Mais le problème persiste car je ne trouve aucun lien de téléchargement pour le bocal.

EDIT 2: Un certain temps est passé depuis que j'ai posé cette question, mais je pensais revenir et ajouter un lien vers le projet Hibernate JPA Model Generator sur SourceForge

Andreï
la source

Réponses:

87

Ce serait génial si quelqu'un connaît également les étapes de configuration dans Eclipse (je suppose que c'est aussi simple que de configurer un processeur d'annotation, mais on ne sait jamais)

Oui, ça l'est. Voici les implémentations et instructions pour les différentes implémentations de JPA 2.0:

EclipseLink

Hiberner

OpenJPA

DonnéesNucleus


La dernière implémentation d'Hibernate est disponible sur:

Une implémentation plus ancienne d'Hibernate se trouve à:

Pascal Thivent
la source
1
Le lien DataNucleus est mort.
Karl Richter
1
Le lien Hibernate est mort aussi
Freelancer
43

Veuillez jeter un œil à jpa-metamodels-with-maven-example .

Hiberner

  • Nous avons besoin org.hibernate.org:hibernate-jpamodelgen.
  • La classe de processeur est org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.

Hibernate en tant que dépendance

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

Hibernate en tant que processeur

      <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

  • Nous avons besoin org.apache.openjpa:openjpa.
  • La classe de processeur est org.apache.openjpa.persistence.meta.AnnotationProcessor6.
  • OpenJPA semble exiger un élément supplémentaire <openjpa.metamodel>true<openjpa.metamodel>.

OpenJPA en tant que dépendance

  <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 en tant que processeur

      <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

  • Nous avons besoin org.eclipse.persistence:org.eclipse.persistence.jpa.modelgen.processor.
  • La classe de processeur est org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor.
  • EclipseLink requiert persistence.xml.

EclipseLink en tant que dépendance

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

EclipseLink en tant que processeur

    <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>

DonnéesNucleus

  • Nous avons besoin org.datanucleus:datanucleus-jpa-query.
  • La classe de processeur est org.datanucleus.jpa.query.JPACriteriaProcessor.

DataNucleus en tant que dépendance

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

DataNucleus en tant que processeur

      <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
la source
3
Juste pour être clair, les éléments générés peuvent être utilisés avec eclipselink, même si vous utilisez hibernate pour le générer, je n'ai pas pu générer de méta-modèle à partir de netbeans 8 et j'ai dû créer un projet de test maven pour générer mes éléments
Kalpesh Soni
@ymajoros Est-il interdit, en SO, de dire something is recommendedsans IMHO? Je ne représente au nom de personne d'autre.
Jin Kwon
1
BTW, voir la réponse de Sorter pour EclipseLink. C'est la configuration que j'utilise depuis des années et cela fonctionne parfaitement. stackoverflow.com/questions/3037593/…
ymajoros
Cette implémentation n'est-elle pas spécifique? J'essaie d'utiliser le métamodèle généré par Hibernate avec EclipseLink et d'obtenir NullPointerException
Michał Ziobro
@ymajoros Il en faut encore un persistence.xml, n'est-ce pas?
Jin Kwon
20

Le support JPA 2.0 d'Eclipse via Dali (qui est inclus dans «Eclipse IDE for JEE Developers») a son propre générateur de métamodèle intégré à Eclipse.

  1. Sélectionnez votre projet dans l' explorateur de packages
  2. Allez dans Propriétés -> Boîte de dialogue JPA
  3. Sélectionner le dossier source de Canonical métamodèle (JPA 2.0) groupe
  4. Cliquez sur le bouton Appliquer pour générer des classes de métamodèles dans le dossier source sélectionné

entrez la description de l'image ici

Cela devrait fonctionner sur n'importe quel fournisseur JPA car les classes générées sont standard.

Voir aussi ici .

James
la source
Existe-t-il un moyen de lancer le processus vous-même? Cela ne produit pas de manière fiable le métamodèle pour moi
thatsIch
6

Pour eclipselink, seule la dépendance suivante est suffisante pour générer un métamodèle. Rien d'autre n'est nécessaire.

    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.5.1</version>
        <scope>provided</scope>
    </dependency>
Trieur
la source
@Barthelomeus votre note est fausse . EclipseLink 2.5.1+ générera également des classes de métamodèles pour les entités non répertoriées, spécifiez simplement <exclude-unlisted-classes>false</exclude-unlisted-classes>dans persisetence.xml
Michele Mariotti
Notez qu'eclipselink ne se générera pas sanspersistence.xml
Jin Kwon
5

Pour Hibernate en tant que fournisseur, ce qui est le plus courant à mon humble avis:

Dans le cas d'outils de construction tels que Gradle, Maven, vous devez avoir le jar Hibernate JPA 2 Metamodel Generator dans le chemin de classe et le niveau du compilateur> = 1.6, c'est tout ce dont vous avez besoin pour construire le projet et le métamodèle sera généré automatiquement.

Dans le cas de l'IDE Eclipse 1. allez à Projet-> Propriétés-> Compilateur Java-> Traitement des annotations et activez-le. 2. Développez Annotation Processing-> Factory Path-> Add External Jar ajouter Hibernate JPA 2 Metamodel Generator jar vérifiez le nouveau jar ajouté et dites OK. Nettoyez et construisez terminé!

Lien Lien vers le jar Hibernate JPA 2 Metamodel Generator à partir du dépôt maven https://mvnrepository.com/artifact/org.hibernate/hibernate-jpamodelgen

SandeepGodara
la source
Dans mon cas, ajouter <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <scope>compile</scope> </dependency> </dependencies>à pom.xml était suffisant.
Lu55
Ai-je besoin des deux configurations lorsque j'utilise maven et Eclipse?
Melkor le
même si hibernate-jpamodelgen a été ajouté dans le pom, je devais le faire et cela a fonctionné
Freelancer
3

Comme il s'agit d'une question très courante, j'ai écrit cet article , sur lequel cette réponse est basée.

Supposons que notre application utilise les éléments suivants Post, PostComment, PostDetailset des Tagentités qui forment un à plusieurs, un à un, et plusieurs à plusieurs relations de table :

Métamodèle des critères JPA

Comment générer le métamodèle de critères JPA

L' hibernate-jpamodelgenoutil fourni par Hibernate ORM peut être utilisé pour analyser les entités du projet et générer le métamodèle de critères JPA. Tout ce que vous devez faire est d' ajouter ce qui suit annotationProcessorPathà maven-compiler-pluginla Maven pom.xmlfichier de configuration:

<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>

Maintenant, lorsque le projet est compilé, vous pouvez voir que dans le targetdossier, les classes Java suivantes sont générées:

> tree target/generated-sources/
target/generated-sources/
└── annotations
    └── com
        └── vladmihalcea
            └── book
                └── hpjp
                    └── hibernate
                        ├── forum
                           ├── PostComment_.java
                           ├── PostDetails_.java
                           ├── Post_.java
                           └── Tag_.java

Métamodèle d'entité de balise

Si l' Tagentité est mappée comme suit:

@Entity
@Table(name = "tag")
public class Tag {

    @Id
    private Long id;

    private String name;

    //Getters and setters omitted for brevity
}

La Tag_classe Metamodel est générée comme ceci:

@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";
}

Le SingularAttributeest utilisé pour les attributs d'entité de base idet name TagJPA.

Métamodèle d'entité de poste

L' Postentité est mappée comme ceci:

@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
}

L' Postentité possède deux attributs de base, idet titleune collection un-à-plusieurs comments, une association un-à-un detailset une collection plusieurs-à-plusieurs tags.

La Post_classe Metamodel est générée comme suit:

@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";
}

Les attributs de base idet title, ainsi que l'association un-à-un details, sont représentés par un SingularAttributetandis que les collections commentset tagssont représentées par le JPA ListAttribute.

Métamodèle d'entité PostDetails

L' PostDetailsentité est mappée comme ceci:

@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
}

Tous les attributs d'entité vont être représentés par le JPA SingularAttributedans la PostDetails_classe Metamodel associée :

@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";
}

Métamodèle d'entité PostComment

Le PostCommentest mappé comme suit:

@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
}

Et, tous les attributs d'entité sont représentés par le JPA SingularAttributedans la PostComments_classe Metamodel associée :

@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";
}

Utilisation du métamodèle de critères JPA

Sans le métamodèle JPA, une requête d'API Criteria qui a besoin de récupérer les PostCommententités filtrées par leur Posttitre associé ressemblerait à ceci:

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();

Notez que nous avons utilisé le postlittéral String lors de la création de l' Joininstance et que nous avons utilisé le titlelittéral String lors du référencement du Post title.

Le métamodèle JPA nous permet d'éviter le codage en dur des attributs d'entité, comme illustré par l'exemple suivant:

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();

L'écriture de requêtes API JPA Criteria est beaucoup plus facile si vous utilisez un outil de complétion de code comme Codota. Consultez cet article pour plus de détails sur le plugin Codota IDE.

Ou, disons que nous voulons récupérer une projection DTO tout en filtrant Post titleles PostDetails createdOnattributs et.

Nous pouvons utiliser le métamodèle lors de la création des attributs de jointure, ainsi que lors de la construction des alias de colonne de projection DTO ou lors du référencement des attributs d'entité que nous devons filtrer:

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();

Cool, non?

Vlad Mihalcea
la source
0

Ok, sur la base de ce que j'ai lu ici, je l'ai fait avec EclipseLink de cette façon et je n'ai pas eu besoin de mettre la dépendance du processeur au projet, uniquement en tant annotationProcessorPathqu'élément du plugin du compilateur.

    <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
la source