Spring Data JPA-fremskrivninger

1. Oversigt

Når du bruger Spring Data JPA til at implementere persistenslaget, returnerer arkivet typisk en eller flere forekomster af rodklassen. Men oftere end ikke har vi ikke brug for alle egenskaberne for de returnerede objekter.

I sådanne tilfælde kan det være ønskeligt at hente data som objekter af tilpassede typer. Disse typer afspejler delvisninger af rodklassen, der kun indeholder egenskaber, vi holder af. Det er her fremskrivninger er nyttige.

2. Første opsætning

Det første trin er at oprette projektet og udfylde databasen.

2.1. Maven afhængigheder

For afhængigheder, se afsnit 2 i denne vejledning.

2.2. Enhedsklasser

Lad os definere to enhedsklasser:

@Entity offentlig klasse Adresse {@Id privat Lang id; @OneToOne privat person person; privat strengstat; private String by; privat String street; privat streng zip-kode; // getters og setters}

Og:

@Entity offentlig klasse person {@Id privat Lang id; privat streng fornavn; privat streng efternavn; @OneToOne (mappedBy = "person") privat adresse adresse; // getters og setters}

Forholdet imellem Person og Adresse enheder er tovejs en-til-en: Adresse er ejersiden, og Person er den omvendte side.

Bemærk i denne vejledning bruger vi en indbygget database - H2.

Når en integreret database er konfigureret, genererer Spring Boot automatisk underliggende tabeller for de enheder, vi definerede.

2.3. SQL-scripts

Vi bruger projection-insert-data.sql script til at udfylde begge understøttelsestabellerne:

INDSÆT I person (id, fornavn, efternavn) VÆRDIER (1, 'John', 'Doe'); INDSÆT I adresse (id, person_id, stat, by, gade, postnummer) VÆRDIER (1,1, 'CA', 'Los Angeles', 'Standford Ave', '90001');

For at rydde op i databasen efter hver testkørsel kan vi bruge et andet script, der hedder projection-clean-up-data.sql:

SLET FRA adresse; SLET FRA person;

2.4. Test klasse

For at bekræfte, at fremskrivninger producerer korrekte data, har vi brug for en testklasse:

@DataJpaTest @RunWith (SpringRunner.class) @Sql (scripts = "/projection-insert-data.sql") @Sql (scripts = "/projection-clean-up-data.sql", executionPhase = AFTER_TEST_METHOD) offentlig klasse JpaProjectionIntegr {// injicerede felter og testmetoder}

Med de givne kommentarer, Spring Boot opretter databasen, injicerer afhængigheder og udfylder og rydder op i tabeller før og efter hver testmetodes udførelse.

3. Interface-baserede fremskrivninger

Når du projicerer en enhed, er det naturligt at stole på en grænseflade, da vi ikke behøver at levere en implementering.

3.1. Lukkede fremskrivninger

Ser tilbage på Adresse klasse, kan vi se den har mange egenskaber, men ikke alle er nyttige. For eksempel er nogle gange et postnummer nok til at angive en adresse.

Lad os erklære en projektionsgrænseflade til Adresse klasse:

offentlig grænseflade AddressView {String getZipCode (); }

Brug det derefter i en lagergrænseflade:

offentlig grænseflade AddressRepository udvider lager {List getAddressByState (strengtilstand); }

Det er let at se, at definere en opbevaringsmetode med en projektionsgrænseflade er stort set den samme som med en enhedsklasse.

Den eneste forskel er det projektionsgrænsefladen i stedet for enhedsklassen bruges som elementtype i den returnerede samling.

Lad os lave en hurtig test af Adresse fremspring:

@Autowired privat AddressRepository addressRepository; @Test offentligt ugyldigt nårUsingClosedProjections_thenViewWithRequiredPropertiesIsReturned () {AddressView addressView = addressRepository.getAddressByState ("CA"). Get (0); assertThat (addressView.getZipCode ()). er EqualTo ("90001"); // ...}

Bag scenen, Spring opretter en proxyinstans af projektionsgrænsefladen for hvert objektobjekt, og alle opkald til proxyen videresendes til det pågældende objekt.

Vi kan bruge fremskrivninger rekursivt. For eksempel er her en projektionsgrænseflade til Person klasse:

offentlig grænseflade PersonView {String getFirstName (); String getLastName (); }

Lad os nu tilføje en metode med returtypen PersonView - en indlejret projektion - i Adresse fremspring:

offentlig grænseflade AddressView {// ... PersonView getPerson (); }

Bemærk, at metoden, der returnerer den indlejrede projektion, skal have samme navn som metoden i rodklassen, der returnerer den relaterede enhed.

Lad os kontrollere indlejrede fremskrivninger ved at tilføje et par udsagn til den testmetode, vi lige har skrevet:

// ... PersonView personView = addressView.getPerson (); assertThat (personView.getFirstName ()). isEqualTo ("John"); assertThat (personView.getLastName ()). isEqualTo ("Doe");

Noter det rekursive fremspring fungerer kun, hvis vi krydser fra ejersiden til den indvendige side. Skulle vi gøre det omvendt, ville den indlejrede projektion blive indstillet til nul.

3.2. Åbn fremskrivninger

Indtil dette tidspunkt har vi gennemgået lukkede fremskrivninger, som angiver projiceringsgrænseflader, hvis metoder nøjagtigt svarer til navnene på enhedsegenskaber.

Der er en anden slags interface-baserede fremskrivninger: åbne fremskrivninger. Disse fremskrivninger giver os mulighed for at definere grænseflademetoder med umatchede navne og med returværdier beregnet ved kørsel.

Lad os gå tilbage til Person projiceringsgrænseflade og tilføj en ny metode:

offentlig grænseflade PersonView {// ... @Value ("# {target.firstName + '' + target.lastName}") String getFullName (); }

Argumentet til @Værdi kommentar er et SPEL-udtryk, hvor mål betegner angiver objektet til understøttende enhed.

Nu definerer vi en anden lagergrænseflade:

offentlig grænseflade PersonRepository udvider lager {PersonView findByLastName (streng efternavn); }

For at gøre det enkelt returnerer vi kun et enkelt projektionsobjekt i stedet for en samling.

Denne test bekræfter åbent fremskrivningsarbejde som forventet:

@Autowired privat PersonRepository personRepository; @Testpublic ugyldigt nårUsingOpenProjections_thenViewWithRequiredPropertiesIsReturned () {PersonView personView = personRepository.findByLastName ("Doe"); assertThat (personView.getFullName ()). er EqualTo ("John Doe"); }

Åbne fremskrivninger har en ulempe: Spring Data kan ikke optimere udførelse af forespørgsler, da de ikke på forhånd ved, hvilke egenskaber der vil blive brugt. Dermed, vi skal kun bruge åbne fremskrivninger, når lukkede fremskrivninger ikke er i stand til at håndtere vores krav.

4. Klassebaserede fremskrivninger

I stedet for at bruge fuldmagter opretter Spring Data for os fra projektionsgrænseflader, vi kan definere vores egne projektionsklasser.

For eksempel er her en projektionsklasse til Person enhed:

offentlig klasse PersonDto {privat streng fornavn; privat streng efternavn; public PersonDto (String firstName, String lastName) {this.firstName = firstName; this.lastName = efternavn; } // getters, lig og hashCode}

For at en projektionsklasse kan fungere sammen med en opbevaringsgrænseflade, skal parameternavne på dens konstruktor matche egenskaberne for rodenhedsklassen.

Vi skal også definere lige med og hashCode implementeringer - de tillader Spring Data at behandle projiceringsobjekter i en samling.

Lad os nu tilføje en metode til Person lager:

offentlig grænseflade PersonRepository udvider lager {// ... PersonDto findByFirstName (String firstName); }

Denne test bekræfter vores klassebaserede projektion:

@Test offentlig ugyldig nårUsingClassBasedProjections_thenDtoWithRequiredPropertiesIsReturned () {PersonDto personDto = personRepository.findByFirstName ("John"); assertThat (personDto.getFirstName ()). er EqualTo ("John"); assertThat (personDto.getLastName ()). isEqualTo ("Doe"); }

Bemærk med den klassebaserede tilgang, vi kan ikke bruge indlejrede fremskrivninger.

5. Dynamiske fremskrivninger

En enhedsklasse kan have mange fremskrivninger. I nogle tilfælde bruger vi muligvis en bestemt type, men i andre tilfælde har vi muligvis brug for en anden type. Nogle gange er vi også nødt til at bruge selve enhedsklassen.

Det er besværligt at definere separate lagergrænseflader eller metoder bare for at understøtte flere returtyper. For at håndtere dette problem giver Spring Data en bedre løsning: dynamiske fremskrivninger.

Vi kan anvende dynamiske fremskrivninger bare ved at erklære en arkivmetode med en Klasse parameter:

offentlig grænseflade PersonRepository udvider lager {// ... T findByLastName (streng efternavn, klassetype); }

Ved at overføre en projektionstype eller enhedsklassen til en sådan metode kan vi hente et objekt af den ønskede type:

@Test offentligt ugyldigt nårUsingDynamicProjections_thenObjectWithRequiredPropertiesIsReturned () {Person person = personRepository.findByLastName ("Doe", Person.class); PersonView personView = personRepository.findByLastName ("Doe", PersonView.class); PersonDto personDto = personRepository.findByLastName ("Doe", PersonDto.class); assertThat (person.getFirstName ()). er EqualTo ("John"); assertThat (personView.getFirstName ()). isEqualTo ("John"); assertThat (personDto.getFirstName ()). er EqualTo ("John"); }

6. Konklusion

I denne artikel gik vi over forskellige typer Spring Data JPA-fremskrivninger.

Kildekoden til denne tutorial er tilgængelig på GitHub. Dette er et Maven-projekt og skal kunne køre som det er.


$config[zx-auto] not found$config[zx-overlay] not found