Delvis opdatering af data med forårsdata

1. Introduktion

Spring Data's CrudRespository # gem er utvivlsomt simpelt, men en funktion kan være en ulempe: Den opdaterer hver kolonne i tabellen. Sådan er U's semantik i CRUD, men hvad hvis vi vil lave en PATCH i stedet?

I denne vejledning skal vi dække teknikker og tilgange til at udføre en delvis i stedet for en fuld opdatering.

2. Problem

Som tidligere nævnt, Gemme() vil overskrive enhver matchet enhed med de leverede data, hvilket betyder, at vi ikke kan levere delvise data. Det kan blive ubelejligt, især for større objekter med mange felter.

Hvis vi ser på en ORM, findes der nogle patches som:

  • Dvale @DynamicUpdate-kommentar, som dynamisk omskriver opdateringsforespørgslen
  • JPA'er @Kolonne kommentar, da vi ikke kan tillade opdateringer om bestemte kolonner ved hjælp af kan opdateres parameter

Men i det følgende vil vi nærme os dette problem med en bestemt hensigt: Vores formål er at forberede vores enheder til Gemme metode uden at stole på en ORM.

3. Vores sag

Lad os først bygge en Kunde enhed:

@Entity offentlig klasse kunde {@Id @GeneratedValue (strategi = GenerationType.AUTO) offentlig lang id; offentligt strengnavn; offentlige String telefon; } 

Derefter definerer vi et simpelt CRUD-arkiv:

@Repository offentlig grænseflade CustomerRepository udvider CrudRepository {Customer findById (lang id); }

Endelig forbereder vi en Kunde service:

@Service offentlig klasse CustomerService {@Autowired CustomerRepository repo; public void addCustomer (String name) {Customer c = new Customer (); c.name = navn; repo.save (c); }}

4. Indlæs og gem tilgang

Lad os først se på en tilgang, der sandsynligvis er kendt: at indlæse vores enheder fra databasen og derefter kun opdatere de felter, vi har brug for.

Selvom dette er simpelt og indlysende, er det af de enkleste tilgange, vi kan bruge.

Lad os tilføje en metode i vores service til at opdatere kontaktoplysningerne til vores kunder.

offentlig ugyldig opdateringCustomerContacts (lang id, strengtelefon) {Kunde myCustomer = repo.findById (id); myCustomer.phone = telefon; repo.save (minKunde); }

Vi ringer til findById metode og hente den matchende enhed, så fortsætter vi og opdaterer de krævede felter og vedvarer dataene.

Denne grundlæggende teknik er effektiv, når antallet af felter, der skal opdateres, er relativt lille, og vores enheder er ret enkle.

Hvad ville der ske med snesevis af felter, der skal opdateres?

4.1. Kortlægningsstrategi

Når vores objekter har et stort antal felter med forskellige adgangsniveauer, er det ret almindeligt at implementere DTO-mønsteret.

Antag nu, at vi har mere end hundrede telefon felter i vores objekt. At skrive en metode, der hælder dataene fra DTO til vores enhed, som vi gjorde før, kunne være generende og ret uvedligeholdelig.

Ikke desto mindre kan vi komme over dette problem ved hjælp af en kortlægningsstrategi og specifikt med MapStruct implementering.

Lad os oprette en CustomerDto:

offentlig klasse CustomerDto {privat lang id; offentligt strengnavn; offentlige String telefon; // ... privat streng telefon99; }

Og også en CustomerMapper:

@Mapper (componentModel = "spring") offentlig grænseflade CustomerMapper {ugyldig updateCustomerFromDto (CustomerDto dto, @MappingTarget Customer entity); }

Det @MappingTarget annotation lader os opdatere et eksisterende objekt, hvilket sparer os for smerter ved at skrive en masse kode.

MapStruct har en @BeanMapping metodedekorator, der lader os definere en regel, der skal springes over nul værdier under kortlægningsprocessen. Lad os tilføje det til vores updateCustomerFromDto metode interface:

@BeanMapping (nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)

Med dette kan vi indlæse lagrede enheder og flette dem med en DTO, før vi ringer til JPA Gemme metode: faktisk opdaterer vi kun de ændrede værdier.

Så lad os tilføje en metode til vores service, som kalder vores kortlægger:

offentlig ugyldig opdateringKunde (CustomerDto dto) {Kund myCustomer = repo.findById (dto.id); mapper.updateCustomerFromDto (dto, myCustomer); repo.save (minKunde); }

Ulempen ved denne tilgang er, at vi ikke kan bestå nul værdier til databasen under en opdatering.

4.2. Enklere enheder

Endelig skal du huske, at vi kan nærme os dette problem fra designfasen af ​​en applikation.

Det er vigtigt at definere vores enheder, så de er så små som muligt.

Lad os se på vores Kunde enhed. Hvad hvis vi strukturerer det en smule og udtrækker alt det telefon felter til KontaktPhone enheder og være under et en-til-mange forhold?

@Entity offentlig klasse CustomerStructured {@Id @GeneratedValue (strategi = GenerationType.AUTO) offentlig Lang id; offentligt strengnavn; @OneToMany (fetch = FetchType.EAGER, targetEntity = ContactPhone.class, mappedBy = "customerId") privat Liste contactPhones; }

Koden er ren, og vigtigere, vi opnåede noget. Nu kan vi opdatere vores enheder uden at skulle hente og udfylde alle telefon data.

Håndtering af små og afgrænsede enheder giver os mulighed for kun at opdatere de nødvendige felter.

Den eneste ulempe ved denne tilgang er, at vi skal designe vores enheder med bevidsthed uden at falde i fælden med overengineering.

5. Brugerdefineret forespørgsel

En anden tilgang, vi kan implementere, er at definere en brugerdefineret forespørgsel til delvise opdateringer.

Faktisk definerer JPA to kommentarer, @Modifying og @Forespørgsel, som giver os mulighed for at skrive vores opdateringserklæring eksplicit.

Vi kan nu fortælle vores ansøgning, hvordan vi opfører os under en opdatering uden at lade byrden på ORM.

Lad os tilføje vores tilpassede opdateringsmetode i lageret:

@Modifying @Query ("opdater Kunden u indstil u.phone =: telefon hvor u.id =: id") ugyldig updatePhone (@Param (værdi = "id") lang id, @Param (værdi = "telefon") Streng telefon); 

Nu kan vi omskrive vores opdateringsmetode:

offentlig ugyldig opdateringCustomerContacts (lang id, streng telefon) {repo.updatePhone (id, telefon); } 

Nu er vi i stand til at udføre en delvis opdatering: med blot et par linier kode og uden at ændre vores enheder har vi nået vores mål.

Ulempen ved denne teknik er, at vi bliver nødt til at definere en metode til hver mulig delvis opdatering af vores objekt.

6. Konklusion

Den delvise dataopdatering er ganske grundlæggende; mens vi kan have vores ORM til at håndtere det, kan det nogle gange være rentabelt at få fuld kontrol over det.

Som vi har set, kan vi forudindlæse vores data og derefter opdatere dem eller definere vores brugerdefinerede udsagn, men husk at være opmærksomme på de ulemper, som disse tilgange indebærer, og hvordan vi kan overvinde dem.

Som normalt er kildekoden til denne artikel tilgængelig på GitHub.