Wie wird die JPA-Entität Metamodel generiert?

94

Im Sinne der mit CriteriaQuery verbundenen Typensicherheit verfügt JPA 2.0 auch über eine API zur Unterstützung der Metamodell- Darstellung von Entitäten.

Ist jemandem eine voll funktionsfähige Implementierung dieser API bekannt (um das Metamodell zu generieren, anstatt die Metamodellklassen manuell zu erstellen)? Es wäre fantastisch, wenn jemand auch die Schritte zum Einrichten in Eclipse kennt (ich nehme an, es ist so einfach wie das Einrichten eines Anmerkungsprozessors, aber Sie wissen es nie).

EDIT: Bin gerade über Hibernate JPA 2 Metamodel Generator gestolpert . Das Problem bleibt jedoch bestehen, da ich keine Download-Links für das Glas finden kann.

EDIT 2: Seit ich diese Frage gestellt habe, ist eine Weile vergangen, aber ich dachte, ich würde zurückkommen und einen Link zum Hibernate JPA Model Generator-Projekt auf SourceForge hinzufügen

Andrey
quelle

Antworten:

87

Es wäre fantastisch, wenn jemand auch die Schritte zum Einrichten in Eclipse kennt (ich nehme an, es ist so einfach wie das Einrichten eines Anmerkungsprozessors, aber Sie wissen es nie).

Ja, so ist es. Hier sind die Implementierungen und Anweisungen für die verschiedenen JPA 2.0-Implementierungen:

EclipseLink

Überwintern

OpenJPA

DataNucleus


Die neueste Hibernate-Implementierung ist verfügbar unter:

Eine ältere Hibernate-Implementierung befindet sich unter:

Pascal Thivent
quelle
1
Der DataNucleus-Link ist tot.
Karl Richter
1
Hibernate Link ist auch tot
Freelancer
43

Bitte werfen Sie einen Blick auf jpa-Metamodelle-mit-Maven-Beispiel .

Überwintern

  • Wir brauchen org.hibernate.org:hibernate-jpamodelgen.
  • Die Prozessorklasse ist org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.

Ruhezustand als Abhängigkeit

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

Ruhezustand als Prozessor

      <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

  • Wir brauchen org.apache.openjpa:openjpa.
  • Die Prozessorklasse ist org.apache.openjpa.persistence.meta.AnnotationProcessor6.
  • OpenJPA scheint ein zusätzliches Element zu erfordern <openjpa.metamodel>true<openjpa.metamodel>.

OpenJPA als Abhängigkeit

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

      <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

  • Wir brauchen org.eclipse.persistence:org.eclipse.persistence.jpa.modelgen.processor.
  • Die Prozessorklasse ist org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor.
  • EclipseLink benötigt persistence.xml.

EclipseLink als Abhängigkeit

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

EclipseLink als Prozessor

    <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

  • Wir brauchen org.datanucleus:datanucleus-jpa-query.
  • Die Prozessorklasse ist org.datanucleus.jpa.query.JPACriteriaProcessor.

DataNucleus als Abhängigkeit

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

DataNucleus als Prozessor

      <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
quelle
3
Um ganz klar zu sein, das generierte Material kann mit Eclipselink verwendet werden, obwohl Sie den Ruhezustand verwenden, um es zu generieren, konnte ich kein Metamodell aus Netbeans 8 generieren und musste ein Maven-Testprojekt erstellen, um mein
Material
@ymajoros Ist es in SO verboten, something is recommendedohne zu sagen IMHO? Ich vertrete nicht im Namen von jemand anderem.
Jin Kwon
1
Übrigens, siehe Sorters Antwort für EclipseLink. Dies ist die Konfiguration, die ich seit Jahren verwende und die perfekt funktioniert. stackoverflow.com/questions/3037593/…
ymajoros
Ist diese Implementierung nicht spezifisch? Ich versuche, das von Hibernate generierte Metamodell mit EclipseLink zu verwenden und NullPointerException
Michał Ziobro
@ymajoros Braucht noch eine persistence.xml, nicht wahr ?
Jin Kwon
20

Die JPA 2.0-Unterstützung von Eclipse über Dali (die in "Eclipse IDE für JEE-Entwickler" enthalten ist) verfügt über einen eigenen Metamodellgenerator, der in Eclipse integriert ist.

  1. Wählen Sie Ihr Projekt im Paket-Explorer aus
  2. Gehen Sie zum Dialogfeld Eigenschaften -> JPA
  3. Wählen Sie den Quellordner aus der Gruppe Canonical Metamodel (JPA 2.0)
  4. Klicken Sie auf die Schaltfläche Übernehmen , um Metamodellklassen im ausgewählten Quellordner zu generieren

Geben Sie hier die Bildbeschreibung ein

Dies sollte auf jedem JPA-Anbieter funktionieren, da die generierten Klassen Standard sind.

Siehe auch hier .

James
quelle
Gibt es eine Möglichkeit, den Prozess selbst zu starten? Dies produziert das Metamodell für mich nicht zuverlässig
19.
6

Für Eclipselink ist nur die folgende Abhängigkeit ausreichend, um ein Metamodell zu generieren. Sonst wird nichts benötigt.

    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.5.1</version>
        <scope>provided</scope>
    </dependency>
Sorter
quelle
@Barthelomeus deine Notiz ist falsch . EclipseLink 2.5.1+ generiert auch Metamodellklassen für nicht aufgelistete Entitäten. <exclude-unlisted-classes>false</exclude-unlisted-classes>Geben Sie dies einfach in persisetence.xml an
Michele Mariotti
Beachten Sie, dass Eclipselink nicht ohnepersistence.xml
Jin Kwon
5

Für den Ruhezustand als Anbieter, der meiner Meinung nach am häufigsten vorkommt:

Bei Build-Tools wie Gradle, Maven muss das Jiber 2 Metamodel Generator-Jiber im Klassenpfad und in der Compiler-Ebene> = 1.6 vorhanden sein. Dies ist alles, was Sie zum Erstellen des Projekts benötigen, und das Metamodell wird automatisch generiert.

Bei IDE Eclipse 1. Gehen Sie zu Projekt-> Eigenschaften-> Java-Compiler-> Anmerkungsverarbeitung und aktivieren Sie diese. 2. Erweitern Sie Annotation Processing-> Factory Path-> Add External Jar. Fügen Sie Hibernate JPA 2 Metamodel Generator hinzu. Überprüfen Sie das neu hinzugefügte Jar und sagen Sie OK. Reinigen und bauen fertig!

Link Hibernate JPA 2 Metamodel Generator JAR-Link von Maven Repo https://mvnrepository.com/artifact/org.hibernate/hibernate-jpamodelgen

SandeepGodara
quelle
In meinem Fall war das Hinzufügen <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <scope>compile</scope> </dependency> </dependencies>zu pom.xml genug.
Lu55
Benötige ich beide Konfigurationen, wenn ich Maven und Eclipse verwende?
Melkor
Obwohl hibernate-jpamodelgen im pom hinzugefügt wurde, musste ich dies tun und es funktionierte
Freelancer
3

Da dies eine sehr häufige Frage ist, habe ich diesen Artikel geschrieben , auf dem diese Antwort basiert.

Nehmen wir an , unsere Anwendung verwendet die folgende Post, PostComment, PostDetails, und TagEinrichtungen, die eine Eins-zu-viele bilden, eins-zu-eins, und many-to-many - Tabelle Beziehungen :

JPA Criteria Metamodel

So generieren Sie das Metamodell für JPA-Kriterien

Mit dem hibernate-jpamodelgenvon Hibernate ORM bereitgestellten Tool können die Projekteinheiten gescannt und das Metamodell für JPA-Kriterien generiert werden. Sie müssen lediglich Folgendes annotationProcessorPathzur maven-compiler-pluginMaven- pom.xmlKonfigurationsdatei hinzufügen :

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

Wenn das Projekt kompiliert wird, können Sie sehen, dass im targetOrdner die folgenden Java-Klassen generiert werden:

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

Tag-Entität Metamodell

Wenn die TagEntität wie folgt zugeordnet ist:

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

    @Id
    private Long id;

    private String name;

    //Getters and setters omitted for brevity
}

Die Tag_Metamodel-Klasse wird folgendermaßen generiert:

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

Das SingularAttributewird für die Basis- idund name TagJPA-Entitätsattribute verwendet.

Post-Entität Metamodell

Die PostEntität wird wie folgt zugeordnet:

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

Die PostEinheit hat zwei grundlegende Eigenschaften, idund title, eine Eins-zu-viele - commentsSammlung, eine Eins-zu-Eins - detailsVerbindung und eine Viele-zu-viele - tagsSammlung.

Die Post_Metamodel-Klasse wird wie folgt generiert:

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

Die Grundlagen idund titleAttribute sowie die Eins-zu-Eins- detailsZuordnung werden durch eine SingularAttributeWeile dargestellt, während die Sammlungen commentsund tagsdurch die JPA dargestellt werden ListAttribute.

PostDetails-Entität Metamodell

Die PostDetailsEntität wird wie folgt zugeordnet:

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

Alle Entitätsattribute werden von der JPA SingularAttributein der zugehörigen PostDetails_Metamodel-Klasse dargestellt:

@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-Entität Metamodell

Das PostCommentist wie folgt abgebildet:

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

Alle Entitätsattribute werden von der JPA SingularAttributein der zugehörigen PostComments_Metamodel-Klasse dargestellt:

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

Verwenden des JPA-Kriterien-Metamodells

Ohne das JPA-Metamodell würde eine Kriterien-API-Abfrage, die die nach dem PostCommentzugehörigen PostTitel gefilterten Entitäten abrufen muss, folgendermaßen aussehen:

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

Beachten Sie, dass wir postbeim Erstellen der JoinInstanz das titleString-Literal und beim Verweisen auf das String-Literal verwendet haben Post title.

Mit dem JPA-Metamodell können wir hartcodierte Entitätsattribute vermeiden, wie im folgenden Beispiel dargestellt:

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

Das Schreiben von API-Abfragen für JPA-Kriterien ist viel einfacher, wenn Sie ein Code-Vervollständigungstool wie Codota verwenden. In diesem Artikel finden Sie weitere Informationen zum Codota IDE-Plugin.

Angenommen, wir möchten eine DTO-Projektion abrufen, während wir die Post titleund die PostDetails createdOnAttribute filtern .

Wir können das Metamodell beim Erstellen der Join-Attribute sowie beim Erstellen der DTO-Projektionsspalten-Aliase oder beim Verweisen auf die Entitätsattribute verwenden, die gefiltert werden müssen:

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, oder?

Vlad Mihalcea
quelle
0

Ok, basierend auf dem, was ich hier gelesen habe, habe ich es mit EclipseLink auf diese Weise gemacht und ich musste die Prozessorabhängigkeit nicht nur als annotationProcessorPathElement des Compiler-Plugins in das Projekt einfügen.

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