Java >> Java tutorial >  >> Tag >> XML

Kortlægningsdefinitioner i JPA og Hibernate – Annoteringer, XML eller begge dele?

Hvis du bruger Hibernate i flere år, kan du måske huske det tidspunkt, hvor du skulle definere alle dine tilknytninger i orm.xml fil. Du havde en masse Java-klasser, der repræsenterede din domænemodel og en lang XML-fil med kort- og forespørgselsoplysninger. En af hovedudfordringerne med den tilgang var, at du havde koden og kortlægningsinformationen i to forskellige filer, som du skulle holde synkroniseret.

Alt det begyndte at ændre sig med introduktionen af ​​annoteringer i Java 5. JPA og Hibernate tilbød annoteringer for at definere entitetstilknytninger og forespørgsler. Kortlægningsdefinitionerne og forespørgslerne blev en del af enhederne. Det lægger al information ét sted og gør det lettere at forstå. Du behøver heller ikke at holde flere filer synkroniseret (hvilket jeg synes er en utrolig fordel...).

2 gyldige muligheder

OK, så nu har vi 2 muligheder for at definere kortlægninger og forespørgsler. Og før du spørger, synes jeg, at de begge er ok. Jeg foretrækker personligt annoteringer frem for XML. Men der er nok projekter derude, som viser, at XML-kortlægninger stadig er en gyldig mulighed. Og at opdele dine domænemodelklasser og kortlægningsdefinitioner kan give sine egne fordele. Hvis du har brug for det, kan du endda gemme kortlægningsdefinitionerne i en konfigurationsfil uden for den installerede jar-fil.

Så lad os tage et mere detaljeret kig på begge tilgange. Og derefter vil jeg vise dig, hvordan du kan bruge begge metoder i det samme projekt og gemme XML-konfigurationen i en ekstern fil på klassestien.

Kortlægningsdefinitioner

Lad os begynde med kortlægningsdefinitionerne. Som sagt kan du enten definere dem via annoteringer eller i en XML-fil.

Grundlæggende enhedskortlægning

Følgende kodestykke viser den enkleste, annotationsbaserede kortlægning. Du tilføjer bare en @Entity annotation til klassen og et @Id annotation til den primære nøgleattribut. Hibernate kortlægger objektet til en databasetabel med samme navn og bruger en standardtilknytning for hver attribut.

@Entity
public class Author {

	@Id
	private Long id;

	…
}

Du kan definere den samme konfiguration med følgende XML-fil.

<entity-mappings>
    <entity class="org.thoughts.on.java.model.Author" name="Author">        
        <attributes>
            <id name="id">
            </id>
        </attributes>
   </entity>
</entity-mappings>

Tilpasset enhedstilknytning

Du kan tilpasse denne standardkortlægning med et sæt annoteringer. Følgende eksempel fortæller Hibernate at kortlægge Forfatteren enhed til forfattertabellen i boghandelsskemaet for at kortlægge id attribut til author_id kolonne og bruge sekvensen author_seq for at generere dens primære nøgleværdi.

@Entity
@Table(name = “author”, schema = “bookstore”)
public class Author {

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = “author_generator”)
	@SequenceGenerator(name=”author_generator”, sequenceName = “author_seq”)
	@Column(name = “author_id”)
	private Long id;

	…
}

Og som i det foregående eksempel kan du selvfølgelig definere den samme mapping i XML.

<entity-mappings>
    <entity class="org.thoughts.on.java.model.Author" name="Author">
    	<table name="author" />
    	<schema name="bookstore" />   
        <attributes>
            <id name="id">
            	<generated-value strategy="sequence" generator="author_generator"/>
            	<column name="author_id"/>
            </id>
        </attributes>
   </entity>
   <sequence-generator name="author_generator" sequence-name="author_seq"/>
</entity-mappings>

Forespørgselsdefinitioner

Du kan også definere navngivne forespørgsler, resultatsæt-tilknytninger, enhedsgrafer osv. via annoteringer og i XML.

Navngivne forespørgsler

Du kan definere en navngivet JPQL-forespørgsel med en @NamedQuery og en navngivet indbygget SQL-forespørgsel med en @NamedNativeQuery anmærkning. Begge følger samme idé. Du definerer forespørgslen én gang og refererer til definitionen ved dens navn for at instantiere den i din virksomhedskode.

Det følgende viser et eksempel på en navngivet JPQL-forespørgsel. Du kan lære mere om indbyggede forespørgsler i min gratis e-bog.

@Entity
@NamedQuery(name = Author.QUERY_SELECT_BY_ID, query = “SELECT a FROM Author a WHERE a.id = :” + Author.PARAM_ID)
public class Author {
	public static final String QUERY_SELECT_BY_ID = “Author.selectById”;
	public static final String PARAM_ID = “id”;

	…
}

Og du kan definere den samme forespørgsel i din orm.xml fil.

<entity-mappings>
  <entity class="org.thoughts.on.java.model.Author" name="Author">
    ...
    <named-query name="Author.selectById">
        <query><![CDATA[
        SELECT a FROM Author a WHERE a.id = :id
        ]]></query>
    </named-query>
  </entity>
  ...
</entity-mappings>

Tilknytning af resultatsæt

Native SQL-forespørgsler giver dig mulighed for at bruge alle SQL-funktioner, der understøttes af din database, men de returnerer et Objekt[] eller en Liste i stedet for de tilknyttede objekter, du får fra en JPQL-forespørgsel. Du kan enten kortlægge disse resultater programmatisk eller definere en resultatsættilknytning og lade Hibernate gøre arbejdet. Jeg vil kun vise et hurtigt eksempel på en sådan kortlægning i dette indlæg. Hvis du vil dykke dybere, bør du tage et kig på min Result Set Mapping-serie. Følgende kodestykke viser en simpel @SqlResultSetMapping definition, der knytter kolonnerne authorId, firstName, lastName og version af det oprindelige forespørgselsresultat til attributterne for Author-entiteten.

@SqlResultSetMapping(
        name = "AuthorMapping",
        entities = @EntityResult(
                entityClass = Author.class,
                fields = {
                    @FieldResult(name = "id", column = "authorId"),
                    @FieldResult(name = "name", column = "name")}))

Hvis du ikke ønsker at definere dette med et sæt annoteringer, kan du også gøre dette med XML-konfiguration.

<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings>
	<entity class="org.thoughts.on.java.model.Author" name="Author">
		...
    
		<sql-result-set-mapping name="AuthorMappingXml">
			<entity-result entity-class="org.thoughts.on.java.model.Author">
				<field-result name="id" column="authorId" />
				<field-result name="name" column="name" />
			</entity-result>
		</sql-result-set-mapping>
	</entity>
	...
</entity-mappings>

Enhedsgrafer

Entitetsgrafer definerer en graf over enheder, som Hibernate henter inden for 1 forespørgsel. Dette er en god tilgang til at undgå n+1 udvalgte problemer og forbedre din applikations ydeevne. Det følgende kodestykke viser den annotationsbaserede definition af en navngiven enhedsgraf, som fortæller Hibernate at hente ordren enhed sammen med alle tilknyttede enheder kortlagt af elementerne attribut.

@Entity
@NamedEntityGraph(name = "graph.Author.books", 
      attributeNodes = @NamedAttributeNode("books"))
public class Author {

  ...
  
  private List<Book> books = new ArrayList<Book>();
  
  ...
}

Som du kan se i det følgende kodestykke, kan du gøre det samme med en XML-konfiguration.

<entity-mappings>
  <entity class="org.thoughts.on.java.model.Author" name="Author">
    ...

    <named-entity-graph name="graph.Author.books">
            <named-attribute-node name="books" />
        </named-entity-graph>
  </entity>
  ...
</entity-mappings>

Annoteringer og XML i samme projekt

Som du har set, kan du definere dine kortlægninger og forespørgsler via annoteringer eller XML. Men hvad sker der, når du bruger XML og anmærkninger i det samme projekt?

Bare for at være klar, anbefaler jeg ikke denne tilgang. Generelt bør du kun bruge én af dem for at holde din kode læsbar og undgå enhver forvirring.

Men det understøttes af JPA-specifikationen, og der er nogle situationer, hvor det kan være nyttigt, f.eks. under overgangen fra en XML-baseret til en annotationsbaseret kortlægningsdefinition eller at tilsidesætte kortlægningen af ​​en delt komponent.

Når du bruger begge tilgange inden for det samme projekt, defineres kortlægningsdefinitionerne af annoteringerne og i orm.xml bruges sammen. XML-konfigurationen tilsidesætter annoteringerne, hvis du definerer den samme tilknytning via annoteringer og i orm.xml fil.

Ekstern XML-konfiguration

Som standard kontrollerer JPA og Hibernate, om en orm.xml fil findes i META-INF bibliotek og indlæs kortlægningsdefinitionerne derfra. Men hvis du vil bruge XML-konfigurationen til at tilsidesætte entitetstilknytningerne af en delt komponent, vil du måske også gemme XML-konfigurationen i en ekstern fil. Du kan gøre det ved at referere til kortlægningskonfigurationen i mapping-filen
attribut i persistence.xml fil.

Følgende eksempel indlæser tilknytningsdefinitionerne fra myMappings.xml fil.

<persistence>
  <persistence-unit name="my-persistence-unit">
    <description>Thougths on Java</description>
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <mapping-file>file:\\\C:\dev\wrk\XmlMapping\XmlMappings\myMappings.xml</mapping-file>
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />

      <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
      <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/test" />
      <property name="javax.persistence.jdbc.user" value="postgres" />
      <property name="javax.persistence.jdbc.password" value="postgres" />

      <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>        
    </properties>
  </persistence-unit>
</persistence>

Oversigt

Som du har set, understøtter JPA og Hibernate annotationsbaserede og XML-baserede kortlægningsdefinitioner.

Som standard indlæser JPA og Hibernate de XML-baserede kortlægningsdefinitioner fra orm.xml fil i META-INF vejviser. Du kan ændre navnet og stien til den fil med mapping-filen attribut i persistence.xml fil.

Du kan bruge dem begge inden for samme projekt. Hvis du gør det, tilsidesætter XML-tilknytningen annoteringerne.


Java tag