Czy potrzebuję elementów <class> w persistence.xml?

110

Mam bardzo prosty plik persistance.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

    <persistence-unit name="eventractor" transaction-type="RESOURCE_LOCAL">
        <class>pl.michalmech.eventractor.domain.User</class>
        <class>pl.michalmech.eventractor.domain.Address</class>
        <class>pl.michalmech.eventractor.domain.City</class>
        <class>pl.michalmech.eventractor.domain.Country</class>

        <properties>
            <property name="hibernate.hbm2ddl.auto" value="validate" />
            <property name="hibernate.show_sql" value="true" />
        </properties>
    </persistence-unit>

</persistence>

i to działa.

Ale kiedy usuwam <class>elementy, aplikacja nie widzi encji (wszystkie klasy są opatrzone adnotacją @Entity).

Czy istnieje automatyczny mechanizm skanowania w poszukiwaniu @Entityzajęć?

Michał Mech
źródło

Odpowiedzi:

78

Plik persistence.xml zawiera rozszerzenie jar-file, którego możesz użyć. Z samouczka Java EE 5 :

<persistence>
    <persistence-unit name="OrderManagement">
        <description>This unit manages orders and customers.
            It does not rely on any vendor-specific features and can
            therefore be deployed to any persistence provider.
        </description>
        <jta-data-source>jdbc/MyOrderDB</jta-data-source>
        <jar-file>MyOrderApp.jar</jar-file>
        <class>com.widgets.Order</class>
        <class>com.widgets.Customer</class>
    </persistence-unit>
</persistence>

Ten plik definiuje jednostkę trwałości o nazwie OrderManagement, która używa źródła danych obsługującego JTA jdbc/MyOrderDB. Elementy jar-filei classokreślają zarządzane klasy trwałości: klasy jednostek, klasy możliwe do osadzenia i mapowane nadklasy. Przez jar-fileelement określa pliki JAR, które są widoczne na zapakowanej jednostce trwałości, które zawierają zarządzanej utrzymywanie klas, natomiast classelementów jawnie nazwy udało utrzymywanie klas.

W przypadku hibernacji spójrz na rozdział 2. Konfiguracja i konfiguracja również, aby uzyskać więcej informacji.

EDYCJA: Właściwie, jeśli nie przeszkadza ci brak zgodności ze specyfikacjami, Hibernate obsługuje automatyczne wykrywanie nawet w Java SE. Aby to zrobić, dodaj hibernate.archive.autodetectionwłaściwość:

<persistence-unit name="eventractor" transaction-type="RESOURCE_LOCAL">
  <!-- This is required to be spec compliant, Hibernate however supports
       auto-detection even in JSE.
  <class>pl.michalmech.eventractor.domain.User</class>
  <class>pl.michalmech.eventractor.domain.Address</class>
  <class>pl.michalmech.eventractor.domain.City</class>
  <class>pl.michalmech.eventractor.domain.Country</class>
   -->

  <properties>
    <!-- Scan for annotated classes and Hibernate mapping XML files -->
    <property name="hibernate.archive.autodetection" value="class, hbm"/>

    <property name="hibernate.hbm2ddl.auto" value="validate" />
    <property name="hibernate.show_sql" value="true" />
  </properties>
</persistence-unit>
Pascal Thivent
źródło
13
Widzę, ale encje (@Entity) są w osobnym projekcie Maven, więc nazwa pliku jar może się zmieniać przy każdej kompilacji. Szukam czegoś do przeskanowania w określonym pakiecie lub ścieżce klas. Jestem zbyt leniwy, aby wpisać wiele, wiele elementów <class> w pliku persistence.xml.
Michał Mech
Na każdej kompilacji ?! Nie zapytam nawet dlaczego, ale ... możesz użyć filtrowania, aby rozwiązać ten problem.
Pascal Thivent
Nie wszyscy dokładnie, ale chcę być odporny na zmiany.
Michał Mech
5
Wiem, starożytny wątek, ale spójrz na wtyczkę jpa-maven-plugin .
Laird Nelson,
Możesz użyć elementu <mapping-file> (który zawiera listę jednostek) w persistence.xml, dzięki czemu możesz zachować tę samą nazwę używanych plików i zintegrować je z budową przywoływanych plików jar.
med_alpa
44

W środowisku Java SE, zgodnie ze specyfikacją, musisz określić wszystkie klasy, tak jak zrobiłeś:

Aby zapewnić przenośność, w środowiskach Java SE należy określić listę wszystkich nazwanych zarządzanych klas trwałości

i

Jeśli nie jest zamierzone, aby klasy trwałości z adnotacjami zawarte w katalogu głównym jednostki trwałości były zawarte w jednostce trwałości, należy użyć elementu exclude-unlisted-classes. Element exclude-unlisted-classes nie jest przeznaczony do użytku w środowiskach Java SE.

(JSR-000220 6.2.1.6)

W środowiskach Java EE nie musisz tego robić, ponieważ dostawca skanuje w poszukiwaniu adnotacji.

Nieoficjalnie możesz spróbować ustawić <exclude-unlisted-classes>false</exclude-unlisted-classes>plik w pliku persistence.xml. Ten parametr ma wartość domyślną falsew EE i trueSE. O ile wiem, zarówno EclipseLink, jak i Toplink obsługują to. Ale nie powinieneś polegać na tym, że działa w SE, zgodnie ze specyfikacją, jak podano powyżej.

Możesz SPRÓBOWAĆ, co następuje (może, ale nie musi działać w środowiskach SE):

<persistence-unit name="eventractor" transaction-type="RESOURCE_LOCAL">
     <exclude-unlisted-classes>false</exclude-unlisted-classes>

    <properties>
            <property name="hibernate.hbm2ddl.auto" value="validate" />
            <property name="hibernate.show_sql" value="true" />
    </properties>
</persistence-unit>
Mads Mobæk
źródło
2
<exclude-unlisted-classes>false</exclude-unlisted-classes>nie działało z WildFly 8.2.1.Final + Hibernate 4.3.7
Andreas Dietrich
12

Czy potrzebuję elementów Class w persistence.xml?

Nie, niekoniecznie. Oto jak to się robi w Eclipse (testowane przez Keplera):

Kliknij prawym przyciskiem myszy projekt, kliknij Właściwości , wybierz JPA , w sekcji Zarządzanie klasami trwałości zaznacz Automatycznie wykryj klasy z adnotacjami .

wprowadź opis obrazu tutaj

abbas
źródło
9
Dlaczego głosować za? OP nawet nie wspomina o Eclipse, a ta odpowiedź nie pokazuje, co ta funkcja Eclipse robi pod maską, aby można było to zrobić bez IDE.
Artem Novikov
2
@Artem Novikov: Uważam to za trudne, ponieważ często pytanie pojawia się w różnych środowiskach, a tutaj chcemy pomóc lub dać przydatne wskazówki! (tak jak dla mnie) Jest to przydatne, ponieważ Eclipse jest powszechnym IDE do programowania w ten sposób, a pod maską nie jest tak ważne, ale myślę, że będzie zawierało wszystkie odpowiednie projekty przestrzeni roboczej (np. mój projekt jest zależny od).
Andreas Dietrich,
stackoverflow.com/questions/17951297/ ... niezła sztuczka, ale najwyraźniej działa tylko wtedy, gdy jednostki kończą się w tym samym module ładującym klasy co persistence.xml
Pierluigi Vernetto
@abbas Proszę pokazać, persistence.xmlże Eclipse generuje.
Frans
12

W przypadku użytkowników korzystających z JPA na wiosnę, począwszy od wersji 3.1, można ustawić packagesToScanwłaściwość poniżej LocalContainerEntityManagerFactoryBeani całkowicie pozbyć się pliku persistence.xml.

Oto podsumowanie

Christopher Yang
źródło
Pracował dla mnie! Scenariusz to wiosna 4 + hibernacja + jpa2 + maven. Testowanie JUnit nie znalazło moich jednostek, ale przy tym ustawieniu wykonało zadanie.
SatA
8

Możesz podać jar-fileścieżkę elementu do folderu ze skompilowanymi klasami. Na przykład dodałem coś takiego, gdy przygotowywałem persistence.xml do niektórych testów integracyjnych:

 <jar-file>file:../target/classes</jar-file>
Arek Trzepacz
źródło
To jest to, czego szukałem!
xtian
Działa też z EclipseLink!
Bombe
8

W przypadku JPA 2+ to załatwia sprawę

 <jar-file></jar-file>

skanowanie wszystkich słoików na wojnie w poszukiwaniu klas @Entity z adnotacjami

eriskooo
źródło
2
czy masz więcej informacji na ten temat? czy to działa przypadkowo, czy jest to napisane w specyfikacji? Czy jest to zależne od implementacji?
markus
skaner jest w klasie rozszerzającej AbstractScannerImpl, hibernacja - nie mam pojęcia, czy to błąd czy funkcja, przepraszam
eriskooo
1
W Javie SE z Hibernate 5.1.2.Final to rozwiązanie nie działa. Hibernate oczekuje nazwy pliku jar ( java.lang.IllegalArgumentException: Unable to visit JAR file:).
Stephan
1
Pracuje! :) z WildFly 8.2.1 Finał + Hibernate 4.3.7 Finał
Andreas Dietrich
Dzięki człowiekowi, dużo szukałem i to jest najładniejsze dostępne rozwiązanie. Wildfly10 + Hibernate 5.0.7 działa.
boutta
7

Hibernate nie obsługuje w <exclude-unlisted-classes>false</exclude-unlisted-classes>ramach SE (inny plakat wspomniał, że działa z TopLink i EclipseLink).

Istnieją narzędzia, które automatycznie generują listę klas do persistence.xml, np. Kreator Importuj schemat bazy danych w IntelliJ. Po uzyskaniu początkowych klas projektu w persistence.xml powinno być łatwe ręczne dodawanie / usuwanie pojedynczych klas w miarę postępu projektu.

Chuck Stephanski
źródło
Automatyczne wykrywanie jednostek w Javie SE po prostu nie jest częścią JPA. Opierające się na tym aplikacje nie są przenośne.
Pascal Thivent,
4

Nie jestem pewien, czy robisz coś podobnego do tego, co robię, ale generuję ładunek javy źródłowej z XSD przy użyciu JAXB w osobnym komponencie przy użyciu Maven. Powiedzmy, że ten artefakt nosi nazwę „model podstawowy”

Chciałem zaimportować ten artefakt zawierający źródło Java i uruchomić hibernację dla wszystkich klas w moim pliku jar artefaktu „modelu podstawowego” i nie określać każdej jawnie. Dodam „model podstawowy” jako zależność dla mojego komponentu hibernacji, ale problem polega na tym, że tag w persistence.xml pozwala tylko na określenie ścieżek bezwzględnych.

Sposób, w jaki sobie z tym poradziłem, polega na jawnym skopiowaniu mojej zależności jar "modelu bazowego" do katalogu docelowego, a także na usunięciu jego wersji. Więc podczas gdy jeśli zbuduję mój artefakt „modelu bazowego”, wygeneruje on „base-model-1.0-SNAPSHOT.jar”, ​​krok kopiowania zasobów kopiuje go jako „base-model.jar”.

Więc w pompie dla komponentu hibernacji:

            <!-- We want to copy across all our artifacts containing java code
        generated from our scheams. We copy them across and strip the version
        so that our persistence.xml can reference them directly in the tag
        <jar-file>target/dependency/${artifactId}.jar</jar-file> -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.5.1</version>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                </execution>
            </executions>       
            <configuration>
                <includeArtifactIds>base-model</includeArtifactIds>
                <stripVersion>true</stripVersion>
            </configuration>        
        </plugin>

Następnie nazywam wtyczkę hibernacji w następnej fazie „klasy procesów”:

            <!-- Generate the schema DDL -->
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>hibernate3-maven-plugin</artifactId>
            <version>2.2</version>

            <executions>
                <execution>
                    <id>generate-ddl</id>
                    <phase>process-classes</phase>
                    <goals>
                        <goal>hbm2ddl</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <components>
                    <component>
                        <name>hbm2java</name>
                        <implementation>annotationconfiguration</implementation>
                        <outputDirectory>/src/main/java</outputDirectory>
                    </component>
                </components>
                <componentProperties>
                    <persistenceunit>mysql</persistenceunit>
                    <implementation>jpaconfiguration</implementation>
                    <create>true</create>
                    <export>false</export>
                    <drop>true</drop>
                    <outputfilename>mysql-schema.sql</outputfilename>
                </componentProperties>
            </configuration>
        </plugin>

i wreszcie w moim persistence.xml mogę wyraźnie ustawić lokalizację jar w ten sposób:

<jar-file>target/dependency/base-model.jar</jar-file>

i dodaj właściwość:

<property name="hibernate.archive.autodetection" value="class, hbm"/>
Spencer Loveridge
źródło
3

To nie jest rozwiązanie, ale wskazówka dla osób korzystających ze Springa:

Próbowałem używać org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBeanz ustawieniem, persistenceXmlLocationale przy tym musiałem podać <class>elementy (nawet jeśli persistenceXmlLocationwłaśnie wskazywały META-INF/persistence.xml).

Gdy nie korzystałem persistenceXmlLocation, mogłem te <class>elementy pominąć .

Ethan Leroy
źródło
Użyłem persistenceXmlLocationwłaściwości w moich LocalContainerEntityManagerFactoryBeanustawieniach. Ale wszystkie zapytania działają, nawet jeśli pominę <class>elementy. Jest na aplikacji Spring / Hibernate / Maven. Ale w swojej wskazówce mówisz, że „Gdy nie używam persistenceXmlLocation, mógłbym pominąć te elementy <class>”. ale dla mnie jest odwrotnie.
Lucky
@Ethan masz rację, ponieważ persistenceXmlLocation nadpisuje packagesToScan - jeśli zajrzysz do źródeł. Więc nie używaj go podczas korzystania z packagesToScan.
Artem Novikov
2

Nie jestem pewien, czy to rozwiązanie jest poniżej specyfikacji, ale myślę, że mogę podzielić się z innymi.

drzewo zależności

my-entity.jar

Zawiera tylko klasy jednostek. Nie META-INF/persistence.xml.

my-services.jar

Zależy od my-entities. Zawiera tylko EJB.

my-resources.jar

Zależy od my-services. Zawiera klasy zasobów i META-INF/persistence.xml.

problemy

  • W jaki sposób możemy określić <jar-file/>element in my-resourcesjako nazwę artefaktu z postfikowaną wersją przejściowej zależności?
  • Jak możemy zsynchronizować wartość <jar-file/>elementu i rzeczywistą zależność przejściową?

rozwiązanie

bezpośrednia (nadmiarowa?) zależność i filtrowanie zasobów

Wstawiłem własność i zależność my-resources/pom.xml.

<properties>
  <my-entities.version>x.y.z-SNAPSHOT</my-entities.version>
</properties>
<dependencies>
  <dependency>
    <!-- this is actually a transitive dependency -->
    <groupId>...</groupId>
    <artifactId>my-entities</artifactId>
    <version>${my-entities.version}</version>
    <scope>compile</scope> <!-- other values won't work -->
  </dependency>
  <dependency>
    <groupId>...</groupId>
    <artifactId>my-services</artifactId>
    <version>some.very.sepecific</version>
    <scope>compile</scope>
  </dependency>
<dependencies>

Teraz persistence.xmlprzygotuj się na filtrowanie

<?xml version="1.0" encoding="UTF-8"?>
<persistence ...>
  <persistence-unit name="myPU" transaction-type="JTA">
    ...
    <jar-file>lib/my-entities-${my-entities.version}.jar</jar-file>
    ...
  </persistence-unit>
</persistence>

Wtyczka Maven Enforcer

Dzięki dependencyConvergenceregule możemy zapewnić, że my-entitieswersja „jest taka sama zarówno w wersji bezpośredniej jak i przechodniej.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <version>1.4.1</version>
  <executions>
    <execution>
      <id>enforce</id>
      <configuration>
        <rules>
           <dependencyConvergence/>
        </rules>
      </configuration>
      <goals>
        <goal>enforce</goal>
      </goals>
    </execution>
  </executions>
</plugin>
Jin Kwon
źródło
0

Nie zawsze we wszystkich przypadkach.

Używam Jboss 7.0.8 i Eclipselink 2.7.0. W moim przypadku, aby załadować encje bez dodawania tego samego w persistence.xml, dodałem następującą właściwość systemową w Jboss Standalone XML:

<property name="eclipselink.archive.factory" value="org.jipijapa.eclipselink.JBossArchiveFactoryImpl"/>

Gandhi
źródło