Gedeeltelijke gegevensupdate met Spring Data

1. Inleiding

Lente data CrudRespository # opslaan is ongetwijfeld eenvoudig, maar één kenmerk kan een nadeel zijn: het werkt elke kolom in de tabel bij. Dat zijn de semantiek van de U in CRUD, maar wat als we in plaats daarvan een PATCH willen doen?

In deze tutorial gaan we in op technieken en benaderingen voor het uitvoeren van een gedeeltelijke in plaats van een volledige update.

2. Probleem

Zoals eerder gezegd, sparen() zal elke overeenkomende entiteit overschrijven met de verstrekte gegevens, wat betekent dat we geen gedeeltelijke gegevens kunnen leveren. Zeker bij grotere objecten met veel velden kan dat lastig worden.

Als we naar een ORM zouden kijken, bestaan ​​er enkele patches, zoals:

  • Overwinteren @DynamicUpdatde annotatie, die de updatevraag dynamisch herschrijft
  • PPV's @Kolom annotatie, omdat we updates voor specifieke kolommen kunnen weigeren met behulp van de bij te werken parameter

Maar in het volgende gaan we dit probleem met een specifieke bedoeling benaderen: Ons doel is om onze entiteiten voor te bereiden op de sparen methode zonder te vertrouwen op een ORM.

3. Onze zaak

Laten we eerst een Klant entiteit:

@Entity openbare klasse Klant {@Id @GeneratedValue (strategie = GenerationType.AUTO) openbare lange id; public String naam; openbare String-telefoon; } 

Vervolgens definiëren we een eenvoudige CRUD-repository:

@Repository openbare interface CustomerRepository breidt CrudRepository {Customer findById (lange id) uit; }

Ten slotte bereiden we een Klantenservice:

@Service openbare klasse CustomerService {@Autowired CustomerRepository-opslagplaats; public void addCustomer (String naam) {Customer c = new Customer (); c.name = naam; repo.save (c); }}

4. Aanpak laden en opslaan

Laten we eerst eens kijken naar een benadering die waarschijnlijk bekend is: onze entiteiten laden uit de database en vervolgens alleen de velden bijwerken die we nodig hebben.

Hoewel dit eenvoudig en voor de hand liggend is, is het een van de eenvoudigste benaderingen die we kunnen gebruiken.

Laten we een methode aan onze service toevoegen om de contactgegevens van onze klanten bij te werken.

openbare ongeldige updateCustomerContacts (lange id, String-telefoon) {Customer myCustomer = repo.findById (id); myCustomer.phone = telefoon; repo.save (myCustomer); }

We zullen de findById methode en halen de overeenkomende entiteit op, dan gaan we verder en werken we de vereiste velden bij en bewaren we de gegevens.

Deze basistechniek is efficiënt wanneer het aantal bij te werken velden relatief klein is en onze entiteiten vrij eenvoudig zijn.

Wat zou er gebeuren met tientallen velden die moeten worden bijgewerkt?

4.1. In kaart brengen van strategie

Wanneer onze objecten een groot aantal velden met verschillende toegangsniveaus hebben, is het vrij gebruikelijk om het DTO-patroon te implementeren.

Stel nu dat we er meer dan honderd hebben telefoon velden in ons object. Het schrijven van een methode die de gegevens van DTO naar onze entiteit stort, zoals we eerder deden, kan hinderlijk en behoorlijk onhoudbaar zijn.

Niettemin kunnen we dit probleem oplossen met behulp van een mappingstrategie, en specifiek met de MapStruct implementatie.

Laten we een CustomerDto:

openbare klasse CustomerDto {privé lange id; public String naam; openbare String-telefoon; // ... privé String phone99; }

En ook een Klantkaart:

@Mapper (componentModel = "spring") openbare interface CustomerMapper {ongeldige updateCustomerFromDto (CustomerDto dto, @MappingTarget Customer-entiteit); }

De @MappingTarget annotatie stelt ons in staat een bestaand object bij te werken, waardoor we niet veel code hoeven te schrijven.

MapStruct heeft een @BeanMapping method decorateur, waarmee we een regel kunnen definiëren om over te slaan nul waarden tijdens het mappingproces. Laten we het toevoegen aan ons updateCustomerFromDto methode interface:

@BeanMapping (nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)

Hiermee kunnen we opgeslagen entiteiten laden en ze samenvoegen met een DTO voordat we JPA aanroepen sparen methode: in feite werken we alleen de gewijzigde waarden bij.

Laten we dus een methode aan onze service toevoegen, die onze mapper zal aanroepen:

openbare ongeldige updateCustomer (CustomerDto dto) {Customer myCustomer = repo.findById (dto.id); mapper.updateCustomerFromDto (dto, myCustomer); repo.save (myCustomer); }

Het nadeel van deze aanpak is dat we niet kunnen slagen nul waarden naar de database tijdens een update.

4.2. Eenvoudigere entiteiten

Houd er ten slotte rekening mee dat we dit probleem vanaf de ontwerpfase van een applicatie kunnen benaderen.

Het is essentieel om onze entiteiten zo klein mogelijk te maken.

Laten we eens kijken naar onze Klant entiteit. Wat als we het een beetje structureren en alle telefoon velden naar Neem contact op met telefoon entiteiten en een een-op-veel-relatie hebben?

@Entity openbare klasse CustomerStructured {@Id @GeneratedValue (strategy = GenerationType.AUTO) openbaar Lange id; public String naam; @OneToMany (fetch = FetchType.EAGER, targetEntity = ContactPhone.class, mappedBy = "customerId") privélijst contactPhones; }

De code is schoon en, nog belangrijker, we hebben iets bereikt. Nu kunnen we onze entiteiten bijwerken zonder alle telefoon gegevens.

Door kleine en begrensde entiteiten af ​​te handelen, kunnen we alleen de noodzakelijke velden bijwerken.

Het enige ongemak van deze aanpak is dat we onze entiteiten bewust moeten ontwerpen, zonder in de val van overengineering te vallen.

5. Aangepaste zoekopdracht

Een andere benadering die we kunnen implementeren, is het definiëren van een aangepaste query voor gedeeltelijke updates.

In feite definieert JPA twee annotaties, @Wijzigen en @Query, waarmee we onze updateverklaring expliciet kunnen schrijven.

We kunnen onze applicatie nu vertellen hoe we zich moeten gedragen tijdens een update, zonder de last op de ORM te laten rusten.

Laten we onze aangepaste updatemethode toevoegen aan de repository:

@Modifying @Query ("update Customer u set u.phone =: phone where u.id =: id") ongeldig updatePhone (@Param (value = "id") lange id, @Param (value = "phone") String telefoon); 

Nu kunnen we onze updatemethode herschrijven:

openbare ongeldige updateCustomerContacts (lange id, String-telefoon) {repo.updatePhone (id, telefoon); } 

Nu kunnen we een gedeeltelijke update uitvoeren: met slechts een paar regels code en zonder onze entiteiten te wijzigen, hebben we ons doel bereikt.

Het nadeel van deze techniek is dat we een methode moeten definiëren voor elke mogelijke gedeeltelijke update van ons object.

6. Conclusie

De gedeeltelijke gegevensupdate is een nogal fundamentele operatie; hoewel we onze ORM kunnen hebben om het af te handelen, kan het soms winstgevend zijn om er volledige controle over te krijgen.

Zoals we hebben gezien, kunnen we onze gegevens vooraf laden en deze vervolgens bijwerken of onze aangepaste verklaringen definiëren, maar onthoud dat u zich bewust bent van de nadelen die deze benaderingen met zich meebrengen en hoe u deze kunt verhelpen.

Zoals gewoonlijk is de broncode voor dit artikel beschikbaar op GitHub.


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