Kihagyás

Determinisztikus sorbarendezés és lapozás

1. A probléma

Gyakori eset, hogy az alkalmazásunkban elérhető adatokat nem mindet egyszerre, ömlesztve szeretnénk megmutatni a felhasználóknak. Ekkor lapozást alkalmazunk, egyszerre csak egy oldalnyi adatotot jelenítünk meg, amelynek méretét lehet állítani és navigálni köztük.

Lapozásról akkor beszélhetünk, ha az adatoknak létezik valamilyen sorrendje. A probléma onnan ered, ha ezügyben rábízzuk magunkat az adatbázisra. Ugyanis azokban az esetekben, amikor mi nem definiálunk egy egyértelmű sorrendet (ez jelentheti azt, hogy egyáltalán nem definiálunk rendezési szabályt vagy amit definiálunk az nem egyértelmű), az adatbázis saját maga fog dönteni, ez pedig a legtöbb esetben nem lesz determinisztikus.

Mivel az adatbázisszerverek által nyújtott alapértelmezett vagy implicit rendezések nem determinisztikusak, ezekre sose támaszkodjunk lapozás esetén. Több adatbázisszerver például nem kezeli megfelelően azt az esetet, amikor több, mint egy oldalnyi adatnál megegyezik az az érték, ami alapján a rendezés történik és egész egyszerűen több oldalon is megjelennek ugyanazok az adatok.

Hol fordulhat elő?

Az általunk megvizsgált adatbázisszerverek közül az SQL Server és a DB2 esetén is fennáll ez a probléma.

2. A megoldás

Ha az adatbázisszerver által nyújtott alapértelmezett megoldás nem megfelelő, egy lehetőségünk van: explicit definiálni egy egyértelmű sorbarendezési szabályt. Az egyértelmű szabály pedig azt jelenti, hogy egyedi értékekkel rendelkező oszlop szerint kell rendeznünk.

Ezzel természetesen nem elimináljuk a felhasználók vagy fejlesztők által igényelt sorbarendezést, csupán pontosítjuk és kiegészítjük azt. Például ha az adminokat Jogosultsági szint szerint rendezzük sorba, lesznek olyan felhasználók amelyek ugyanazzal a jogosultsági szinttel rendelkeznek. Ilyenkor ha nem egészítjük ki a rendezést egy egyedi értékkel, akkor a jogosultsági szinteken belül nem lesz determinisztikus a sorrend és duplikált megjelenítéshez vezethet lapozáskor.

3. A technikai megvalósítás

A megoldáshoz használható egyedi értékre legalkalmasabb oszlop általában az, ami elsődleges kulcsként is szolgál, a legtöbb esetben ez pedig az ID. Alapértelmezetten a semi-product ezen oszlop rendezésével egészíti ki a beérkező kéréseket.

A kiegészítés a Controllerbe érkezést megelőzően történik meg, mégpedig az automatikasan beolvasott Pageable és Sort objektumok esetén. Ez a funkció kikapcsolható/bekapcsolható és a rendezéshez használt mező felülírható globálisan, valamint a globális beállítás felülírható végpontonként is.

3.1 Globális konfiguráció

Az application.yml-ben szereplő beállítás minden végpontra vonatkozik. Itt látható az alapértelmezett beállítás.

project:
  techcore:
    default-sort:
      enabled: true
      default-property: id

3.2 Végpont konfiguráció

Amennyiben felül szeretnénk írni a globális beállítást, a @DefaultSort annotációt kell használnunk a végponthoz tartozó Controller metódusban, mégpedig közvetlenül a Pageable vagy Sort objektumokra.

Az annotáció két paraméterrel rendelkezik:

  • enabled: Adott végponton megtörténjen-e a sorbarendezés kiegészítése. Alapértelmezetten true. Akkor is bekapcsolható egy adott végpontra, ha globálisan ki van kapcsolva a funkció.
  • property: Mely mező szerint történjen meg a sorbarendezés kiegészítése. Csak akkor szükséges definiálni, ha az application.yml-ben szereplő értéket módosítani szeretnénk.

Példa arra, hogy egy végpont esetén más mező szerint szeretnénk kiegészíteni sorbarendezést:

@RestController
public class AdminManagementController implements AdminManagementApi {
    @Override
    @PreAuthorize("hasAuthority('ADMIN_GET_ADMINS')")
    public ResponseEntity<AdminsResponse> getAdmins(@DefaultSort(property = "creationDateTime") Pageable pageable) {
        ...
    }
}