Jak wygenerować metamodel encji JPA?

96

W duchu bezpieczeństwa typu związanego z CriteriaQuery JPA 2.0 posiada również API do obsługi reprezentacji encji przez Metamodel .

Czy ktoś jest świadomy w pełni funkcjonalnej implementacji tego API (do generowania metamodelu w przeciwieństwie do ręcznego tworzenia klas metamodelu)? Byłoby wspaniale, gdyby ktoś również znał kroki konfigurowania tego w Eclipse (zakładam, że jest to tak proste, jak skonfigurowanie procesora adnotacji, ale nigdy nie wiadomo).

EDYCJA: Właśnie natknąłem się na Hibernate JPA 2 Metamodel Generator . Ale problem pozostaje, ponieważ nie mogę znaleźć żadnych linków do pobrania słoika.

EDYCJA 2: Minęło trochę czasu, odkąd zadałem to pytanie, ale pomyślałem, że wrócę i dodam link do projektu Hibernate JPA Model Generator na SourceForge

Andrey
źródło

Odpowiedzi:

87

Byłoby wspaniale, gdyby ktoś również znał kroki konfigurowania tego w Eclipse (zakładam, że jest to tak proste, jak skonfigurowanie procesora adnotacji, ale nigdy nie wiadomo)

Tak to jest. Oto implementacje i instrukcje dla różnych implementacji JPA 2.0:

EclipseLink

Hibernować

OpenJPA

DataNucleus


Najnowsza implementacja Hibernate jest dostępna pod adresem:

Starsza implementacja Hibernate jest dostępna pod adresem:

Pascal Thivent
źródło
1
Łącze DataNucleus nie działa.
Karl Richter,
1
Link do hibernacji też jest martwy
Freelancer
43

Proszę spojrzeć na przykład jpa-metamodels-with-maven .

Hibernować

  • Potrzebujemy org.hibernate.org:hibernate-jpamodelgen.
  • Klasa procesora to org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.

Hibernacja jako zależność

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

Hibernuj jako procesor

      <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

  • Potrzebujemy org.apache.openjpa:openjpa.
  • Klasa procesora to org.apache.openjpa.persistence.meta.AnnotationProcessor6.
  • Wydaje się, że OpenJPA wymaga dodatkowego elementu <openjpa.metamodel>true<openjpa.metamodel>.

OpenJPA jako zależność

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

      <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

  • Potrzebujemy org.eclipse.persistence:org.eclipse.persistence.jpa.modelgen.processor.
  • Klasa procesora to org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor.
  • EclipseLink wymaga persistence.xml.

EclipseLink jako zależność

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

EclipseLink jako procesor

    <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

  • Potrzebujemy org.datanucleus:datanucleus-jpa-query.
  • Klasa procesora to org.datanucleus.jpa.query.JPACriteriaProcessor.

DataNucleus jako zależność

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

DataNucleus jako procesor

      <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
źródło
3
Żeby było jasne, wygenerowane rzeczy mogą być używane z eclipselink, mimo że używasz hibernacji do ich generowania, nie mogłem wygenerować meta modelu z netbeans 8 i musiałem stworzyć projekt testowy maven, aby wygenerować moje rzeczy
Kalpesh Soni
@ymajoros Czy w SO nie wolno mówić something is recommendedbez IMHO? Nie reprezentuję w imieniu nikogo innego.
Jin Kwon
1
BTW, zobacz odpowiedź Sortera na EclipseLink. To konfiguracja, której używam od lat i działa idealnie. stackoverflow.com/questions/3037593/…
ymajoros
Czy ta implementacja nie jest specyficzna, próbuję użyć metamodelu wygenerowanego przez Hibernate z EclipseLink i uzyskać NullPointerException
Michał Ziobro
@ymajoros Nadal potrzebuje persistence.xml, prawda?
Jin Kwon
20

Wsparcie Eclipse JPA 2.0 przez Dali (które jest zawarte w "Eclipse IDE for JEE Developers") ma własny generator metamodeli zintegrowany z Eclipse.

  1. Wybierz projekt w Eksploratorze pakietów
  2. Przejdź do Właściwości -> Okno dialogowe JPA
  3. Wybierz folder źródłowy z Canonical metamodelu (JPA 2.0) grupa
  4. Kliknij przycisk Zastosuj , aby wygenerować klasy metamodelu w wybranym folderze źródłowym

wprowadź opis obrazu tutaj

Powinno to działać na każdym dostawcy JPA, ponieważ wygenerowane klasy są standardowe.

Zobacz także tutaj .

James
źródło
Czy jest jakiś sposób, aby samemu rozpocząć ten proces? To nie daje mi wiarygodnie metamodelu
znaczy
6

W przypadku eclipselink do wygenerowania metamodelu wystarczy tylko następująca zależność. Nic więcej nie jest potrzebne.

    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.5.1</version>
        <scope>provided</scope>
    </dependency>
Sorter
źródło
@Barthelomeus twoja notatka jest fałszywa . EclipseLink 2.5.1+ wygeneruje klasy metamodel dla podmiotów nienotowanych też, wystarczy określić <exclude-unlisted-classes>false</exclude-unlisted-classes>w persisetence.xml
Michele Mariotti
Zauważ, że eclipselink nie wygeneruje się bezpersistence.xml
Jin Kwon
6

Dla Hibernate jako dostawcy, który jest najczęściej używanym IMHO:

W przypadku narzędzi do budowania, takich jak Gradle, Maven, musisz mieć słoik Hibernate JPA 2 Metamodel Generator w ścieżce klas i poziomie kompilatora> = 1.6, czyli wszystko, czego potrzebujesz, aby zbudować projekt, a metamodel zostanie wygenerowany automatycznie.

W przypadku środowiska IDE Eclipse 1. przejdź do Projekt-> Właściwości-> Kompilator Java-> Przetwarzanie adnotacji i włącz je. 2. Rozwiń Annotation Processing-> Factory Path-> Add External Jar dodaj Hibernate JPA 2 Metamodel Generator jar sprawdź nowo dodany jar i powiedz OK. Wyczyść i zbuduj gotowe!

Link Hibernate JPA 2 Metamodel Generator link z repozytorium maven https://mvnrepository.com/artifact/org.hibernate/hibernate-jpamodelgen

SandeepGodara
źródło
W moim przypadku <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <scope>compile</scope> </dependency> </dependencies>wystarczyło dodanie do pom.xml.
Lu55
Czy potrzebuję obu konfiguracji podczas korzystania z maven i Eclipse?
Melkor
mimo że hibernate-jpamodelgen został dodany do pom, musiałem to zrobić i zadziałało
Freelancer
To również powinna być akceptowana odpowiedź. Wielkie dzięki Sandeep
Shubham Arya
3

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 :

Metamodel kryteriów JPA

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;

    //Getters and setters omitted for brevity
}

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

    //Getters and setters omitted for brevity
}

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;

    //Getters and setters omitted for brevity
}

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;

    //Getters and setters omitted for brevity
}

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?

Vlad Mihalcea
źródło
0

Ok, bazując na tym co tutaj przeczytałem, zrobiłem to z EclipseLink w ten sposób i nie musiałem wstawiać zależności procesora do projektu, tylko jako annotationProcessorPathelement wtyczki kompilatora.

    <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
źródło