Java >> Java Tutorial >  >> Tag >> Spring

Verwenden von jOOQ mit Spring:Sortieren und Paginieren

JOOQ ist eine Bibliothek, die uns hilft, die Kontrolle über unser SQL zu erlangen. Es kann Code aus unserer Datenbank generieren und uns helfen, typsichere Datenbankabfragen zu erstellen, indem es seine fließende API verwendet.

In den früheren Teilen dieses Tutorials haben wir gelernt, wie wir den Anwendungskontext unserer Anwendung konfigurieren, Code aus unserer Datenbank generieren und CRUD-Operationen zu unserem jOOQ-Repository hinzufügen können.

Dieses Mal werden wir lernen, wie wir eine einfache Suchfunktion implementieren können, die Sortierung und Paginierung unterstützt.

Fangen wir an.

Hinzufügen von Paginierungs- und Sortierunterstützung zur Webschicht

Wenn wir eine Suchfunktion implementieren, die sowohl Paginierung als auch Sortierung unterstützen muss, müssen wir einen Weg finden, unserem Backend die Seitennummer, die Seitengröße, den Namen des Sortierfelds und die Sortierreihenfolge bereitzustellen.

Wir könnten natürlich eine Komponente implementieren, die dies unterstützt, aber es ist nicht so einfach, wie es sich anhört. Es ist ziemlich einfach, einen HandlerMethodArgumentResolver zu erstellen der diese Informationen aus einer HTTP-Anforderung extrahiert und in ein Objekt konvertiert, das dann als Methodenargument an unsere Controller-Methode weitergeleitet wird. Das Problem ist, dass es viele "außergewöhnliche" Situationen gibt, die diese Aufgabe ziemlich schwierig machen. Zum Beispiel

  • Wenn diese Informationen nicht von der HTTP-Anfrage gefunden werden, müssen wir auf Standardwerte zurückgreifen.
  • Wenn die erforderlichen Informationen fehlen (z. B. wird die Seitenzahl ohne Angabe der Seitengröße angegeben), müssen wir entweder auf Standardwerte zurückgreifen oder einen Fehler an den Benutzer unserer REST-API zurückgeben.

Glücklicherweise müssen wir diese Komponente nicht implementieren. Das Spring Data Commons-Projekt verfügt über eine Komponente, die Paging- und Sortierinformationen aus HTTP-Anforderungen extrahiert und es uns ermöglicht, diese Informationen in Controller-Methoden einzufügen.

Lassen Sie uns herausfinden, dass wir die Spring Data Commons-Binärdateien mit Maven erhalten können.

Erforderliche Abhängigkeiten mit Maven erhalten

Wir können die erforderlichen Binärdateien mit Maven erhalten, indem wir die folgende Abhängigkeitserklärung zu den Abhängigkeiten hinzufügen Abschnitt unserer POM-Datei:

<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-commons</artifactId>
	<version>1.7.1.RELEASE</version>
</dependency>

Unser nächster Schritt besteht darin, einige Änderungen an der Anwendungskontextkonfiguration unserer Beispielanwendung vorzunehmen. Lassen Sie uns weitermachen und herausfinden, welche Art von Änderungen wir vornehmen müssen.

Konfigurieren des Anwendungskontexts

Wir können die Web-Paginierungsunterstützung von Spring Data aktivieren, indem wir eine einfache Änderung an der Anwendungskontext-Konfigurationsklasse vornehmen, die die Webschicht unserer Beispielanwendung konfiguriert. Wir müssen die Konfigurationsklasse mit @EnableSpringDataWebSupport annotieren Anmerkung. Dadurch wird sichergestellt, dass die erforderlichen Beans automatisch registriert werden.

Der relevante Teil des WebAppContext Klasse sieht wie folgt aus:

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@ComponentScan({
        "net.petrikainulainen.spring.jooq.common.controller",
        "net.petrikainulainen.spring.jooq.todo.controller"
})
@EnableWebMvc
@EnableSpringDataWebSupport
public class WebAppContext extends WebMvcConfigurerAdapter {
	//Other methods are omitted for the sake of clarity
}

Das ist es. Wir haben nun die erforderlichen Änderungen an der Anwendungskontextkonfiguration unserer Beispielanwendung vorgenommen. Lassen Sie uns herausfinden, wie wir die Web-Paginierungsunterstützung in unserer Anwendung nutzen können.

Web-Paginierung verwenden

Wenn wir unsere Abfrageergebnisse sortieren und paginieren möchten, müssen wir die folgenden Schritte ausführen:

  1. Fügen Sie die Paging- und Sortierkonfiguration zur HTTP-Anforderung hinzu.
  2. Fügen Sie ein Pageable hinzu Methodenparameter an die Controller-Methode.

Zuerst , können wir die Paging- und Sortierkonfiguration zur HTTP-Anforderung hinzufügen, indem wir die folgenden Anforderungsparameter verwenden:

  • Die Seite Anfrageparameter gibt die angeforderte Seitenzahl an.
  • Die Größe Anfrageparameter gibt die Größe der angeforderten Seite an.
  • Die Sortierung Der Anforderungsparameter gibt die Eigenschaften an, die zum Sortieren der Abfrageergebnisse verwendet werden. Dieser Wert dieses Anforderungsparameters muss dieser Syntax folgen:property,property(,ASC|DESC) . Wenn die Sortierrichtung nicht angegeben ist, werden die Ergebnisse aufsteigend sortiert. Wenn Sie die Sortierreihenfolge ändern möchten, müssen Sie mehrere Sortierparameter verwenden (z. B. ?sort=title&sort=id,desc ).

Zweite , müssen wir ein Pageable hinzufügen Methodenparameter zu unserer Controller-Methode. Der relevante Teil des TodoController Klasse sieht wie folgt aus:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.List;


@RestController
@RequestMapping("/api/todo")
public class TodoController {

    private final TodoCrudService crudService;

    private final TodoSearchService searchService;

    @Autowired
    public TodoController(TodoCrudService crudService, TodoSearchService searchService) {
        this.crudService = crudService;
        this.searchService = searchService;
    }

    @RequestMapping(value = "/search", method = RequestMethod.GET)
    public Page<TodoDTO> findBySearchTerm(@RequestParam("searchTerm") String searchTerm, Pageable pageable) {
        return searchService.findBySearchTerm(searchTerm, pageable);
    }
}

Wir können jetzt die Suchfunktion zu unserem jOOQ-Repository hinzufügen. Lassen Sie uns herausfinden, wie das gemacht wird.

Implementierung der Repository-Schicht

Als erstes müssen wir dem TodoRepository eine neue öffentliche Methode hinzufügen Schnittstelle. Der findBySearchTerm(String searchTerm, Pageable pageable) Die Methode findet die Aufgabeneinträge, deren Titel oder Beschreibung den angegebenen Suchbegriff enthält, und gibt die Abfrageergebnisse zurück, indem sie der als Methodenparameter angegebenen Paging- und Sortierkonfiguration folgt.

Der relevante Teil des TodoRepository Die Benutzeroberfläche sieht wie folgt aus:

import org.springframework.data.domain.Pageable;

import java.util.List;

public interface TodoRepository {

    public Page<Todo> findBySearchTerm(String searchTerm, Pageable pageable);

	//Other methods are omitted for the sake of clarity
}

Die Implementierung dieser Methode hat drei Verantwortlichkeiten:

  1. Es muss die Aufgabeneinträge finden, deren Titel oder Beschreibung den angegebenen Suchbegriff enthält.
  2. Es muss die Sortier- und Paging-Optionen verarbeiten, die von Pageable gefunden werden Objekt und transformiere sie in eine Form, die von jOOQ verstanden wird.
  3. Es muss die zurückgegebene Seite erstellen Objekt. Dieses Objekt enthält Informationen über die zurückgegebene Seite und die tatsächlichen Suchergebnisse.

Lassen Sie uns weitermachen und herausfinden, wie wir Aufgabeneinträge finden können, deren Titel oder Beschreibung den angegebenen Suchbegriff enthält.

Implementieren der Suchanfrage

Wir können die Suchanfrage folgendermaßen implementieren:

  1. Fügen Sie findBySearchTerm(String searchTerm, Pageable pageable) hinzu -Methode in das JOOQTodoRepository Klasse.
  2. Kommentieren Sie die Methode mit @Transactional -Anmerkung und legen Sie den Wert von readOnly fest Attribut auf wahr. Dadurch wird sichergestellt, dass die SELECT -Anweisung wird innerhalb einer schreibgeschützten Transaktion ausgeführt.
  3. Implementieren Sie findBySearchTerm() Methode, indem Sie die folgenden Schritte ausführen:
    1. Erstellen Sie den ähnlichen Ausdruck, der in unserer Datenbankabfrage verwendet wird.
    2. Erstellen Sie ein neues SELECT -Anweisung durch Aufrufen von selectFrom(Table table) Methode des DSLContext Benutzeroberfläche und geben Sie an, dass Sie Informationen aus den Aufgaben auswählen möchten Tabelle.
    3. Geben Sie das WO an -Klausel von SELECT -Anweisung durch Aufrufen der where(Condition... conditions) Methode des SelectWhereStep Schnittstelle. Erstellen Sie den Methodenparameter dieser Methode, indem Sie die folgenden Schritte ausführen:
      1. Erstellen Sie ähnliche Bedingungen für die Beschreibung und Titel Spalten der Aufgaben Tabelle durch Aufrufen von likeIgnoreCase(String value) Methode des Feldes Schnittstelle. Übergeben Sie den erstellten Like-Ausdruck als Methodenparameter.
      2. Kombinieren Sie die erstellten Like-Bedingungen, indem Sie or(Condition other) verwenden Methode der Bedingung Schnittstelle.
    4. Erhalten Sie eine Liste von TodosRecord Objekte durch Aufrufen von fetchInto(Class type) Methode der ResultQuery Schnittstelle. Übergeben Sie eine TodosRecord.class Objekt als Methodenparameter.
    5. Transformiere die Liste von TodosRecord Objekte in eine Liste von Todo Objekte durch Aufrufen des privaten convertQueryResultsToModelObjects() Methode. Diese Methode durchläuft die Liste von TodosRecord Objekte und konvertiert jeden TodosRecord Objekt in ein Todo -Objekt durch Aufrufen von convertQueryResultToModelObject() Methode. Jede Aufgabe Objekt wird einer Liste hinzugefügt, die zurückgegeben wird, wenn alle TodosRecord Objekte wurden verarbeitet.
    6. Erstellen Sie eine neue PageImpl Objekt und übergeben Sie die Liste von Todo Objekte als Konstruktorargument.
    7. Gib die erstellte PageImpl zurück Objekt.

Der Quellcode unserer Implementierung sieht wie folgt aus:

import org.jooq.DSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

import static net.petrikainulainen.spring.jooq.todo.db.tables.Todos.TODOS;

@Repository
public class JOOQTodoRepository implements TodoRepository {

    private final DateTimeService dateTimeService;

    private final DSLContext jooq;

    //The constructor is omitted for the sake of clarity

    @Transactional(readOnly = true)
    @Override
    public Page<Todo> findBySearchTerm(String searchTerm, Pageable pageable) {
        String likeExpression = "%" + searchTerm + "%";

        List<TodosRecord> queryResults = jooq.selectFrom(TODOS)
                .where(
                        TODOS.DESCRIPTION.likeIgnoreCase(likeExpression)
                                .or(TODOS.TITLE.likeIgnoreCase(likeExpression))
                )
                .fetchInto(TodosRecord.class);

        List<Todo> todoEntries = convertQueryResultsToModelObjects(queryResults);
		return new PageImpl<>(todoEntries);
    }

    private List<Todo> convertQueryResultsToModelObjects(List<TodosRecord> queryResults) {
        List<Todo> todoEntries = new ArrayList<>();

        for (TodosRecord queryResult : queryResults) {
            Todo todoEntry = convertQueryResultToModelObject(queryResult);
            todoEntries.add(todoEntry);
        }

        return todoEntries;
    }

    private Todo convertQueryResultToModelObject(TodosRecord queryResult) {
        return Todo.getBuilder(queryResult.getTitle())
                .creationTime(queryResult.getCreationTime())
                .description(queryResult.getDescription())
                .id(queryResult.getId())
                .modificationTime(queryResult.getModificationTime())
                .build();
    }
	
	//Other methods are omitted for the sake of clarity
}

Wir haben jetzt eine Repository-Methode erstellt, die Todo-Einträge in der Datenbank sucht. Als nächstes sortieren wir die Abfrageergebnisse dieser Datenbankabfrage.

Sortieren der Abfrageergebnisse

Bevor wir die Abfrageergebnisse unserer Suchanfrage sortieren können, müssen wir verstehen, wie wir die Sortieroptionen unserer Datenbankabfrage aus dem Pageable erhalten können Objekt.

  • Wir können einen Verweis auf eine Sortierung erhalten Objekt durch Aufrufen von getSort() Methode des Pageable Schnittstelle. Dieses Objekt enthält die Sortieroptionen, die von der HTTP-Anforderung gefunden wurden.
  • Die Sortierung Objekt kann null oder mehr Sortieroptionen enthalten. Der Iterator() Methode der Sortierung Klasse gibt einen Iterator zurück Objekt, das wir verwenden können, wenn wir jede Sortieroption unserer Datenbankabfrage verarbeiten möchten.
  • Die Sortierung class enthält den Eigenschaftsnamen und die Sortierrichtung.

Mit anderen Worten, wir müssen die folgenden Anforderungen erfüllen:

  • Wir müssen eine Situation unterstützen, in der keine Sortieroptionen angegeben sind.
  • Wir müssen eine Situation unterstützen, in der unsere Abfrageergebnisse mithilfe mehrerer Spalten sortiert werden.
  • Wir müssen davon ausgehen, dass jede Spalte ihre eigene Sortierreihenfolge hat.

Wir können diese Anforderungen erfüllen, indem wir die folgenden Änderungen am JOOQTodoRepository vornehmen Klasse:

  1. Fügen Sie ein privates getTableField(String sortFieldName) hinzu -Methode in die Repository-Klasse und implementieren Sie diese Methode, indem Sie die folgenden Schritte ausführen:
    1. Verwenden Sie Reflektion, um das Feld zu erhalten Objekt, das Informationen über das angeforderte Feld des TODOS liefert Klasse.
    2. Wenn das Feld nicht gefunden wird oder wir nicht darauf zugreifen können, lösen Sie eine neue InvalidDataAccessApiUsageException aus .
    3. Wenn das Feld gefunden wird, wandeln Sie das zurückgegebene Feld um Objekt in ein TableField Objekt und gib es zurück.
  2. Fügen Sie ein privates convertTableFieldToSortField(TableField tableField, Sort.Direction sortDirection) hinzu -Methode in die Repository-Klasse und implementieren Sie die Methode, indem Sie die folgenden Schritte ausführen:
    1. Wenn die Sortierreihenfolge dieses Feldes aufsteigend ist, rufen Sie asc() auf Methode des Feldes Schnittstelle und geben das zurückgegebene Objekt zurück.
    2. Rufen Sie andernfalls desc() auf Methode des Feldes Schnittstelle und geben das zurückgegebene Objekt zurück.
  3. Fügen Sie ein privates getSortFields(Sort sortSpecification) hinzu -Methode in die Repository-Klasse und implementieren Sie sie, indem Sie die folgenden Schritte ausführen:
    1. Erstellen Sie eine neue Sammlung die SortField enthält Objekte.
    2. Wenn die Sortieroptionen nicht gefunden werden, geben Sie eine leere Sammlung zurück Objekt.
    3. Iterieren Sie die Sort.Order gefundene Objekte vom Sortieren Objekt, das als Methodenparameter angegeben ist, und verarbeitet jede Sort.Order Objekt, indem Sie die folgenden Schritte ausführen:
      1. Wandle jede Sort.Order um Objekt in ein SortField -Objekt mithilfe von getTableField() und convertTableFieldToSortField() Methoden.
      2. Fügen Sie jedes SortField hinzu Objekt zur Sammlung die in Schritt eins erstellt wurde.
    4. Gib die Sammlung zurück von SortField Objekte.
  4. Sortieren Sie die Abfrageergebnisse, indem Sie diesen Schritten folgen:
    1. Erhalten Sie die Sortierung Objekt durch Aufrufen von getSort() Methode des Pageable Schnittstelle.
    2. Holen Sie sich die Sammlung> Objekt durch Aufrufen von getSortFields() Methode. Übergeben Sie das Sortieren Objekt als Methodenparameter.
    3. Erstellen Sie eine ORDER BY -Klausel durch Aufrufen der orderBy(Collection>-Felder) Methode des SelectSeekStepN -Schnittstelle und übergeben Sie die Collection> Objekt als Methodenparameter.

Der Quellcode unserer Implementierung sieht wie folgt aus (die relevanten Teile sind hervorgehoben):

import org.jooq.DSLContext;
import org.jooq.SortField;
import org.jooq.TableField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import static net.petrikainulainen.spring.jooq.todo.db.tables.Todos.TODOS;

@Repository
public class JOOQTodoRepository implements TodoRepository {

    private final DateTimeService dateTimeService;

    private final DSLContext jooq;

	//The constructor is omitted for the sake of clarity

    @Transactional(readOnly = true)
    @Override
    public Page<Todo> findBySearchTerm(String searchTerm, Pageable pageable) {
        String likeExpression = "%" + searchTerm + "%";

        List<TodosRecord> queryResults = jooq.selectFrom(TODOS)
                .where(
                        TODOS.DESCRIPTION.likeIgnoreCase(likeExpression)
                                .or(TODOS.TITLE.likeIgnoreCase(likeExpression))
                )
                .orderBy(getSortFields(pageable.getSort()))
                .fetchInto(TodosRecord.class);

        List<Todo> todoEntries = convertQueryResultsToModelObjects(queryResults);
		return new PageImpl<>(todoEntries);
    }

    private Collection<SortField<?>> getSortFields(Sort sortSpecification) {
        Collection<SortField<?>> querySortFields = new ArrayList<>();

        if (sortSpecification == null) {
            return querySortFields;
        }

        Iterator<Sort.Order> specifiedFields = sortSpecification.iterator();

        while (specifiedFields.hasNext()) {
            Sort.Order specifiedField = specifiedFields.next();

            String sortFieldName = specifiedField.getProperty();
            Sort.Direction sortDirection = specifiedField.getDirection();

            TableField tableField = getTableField(sortFieldName);
            SortField<?> querySortField = convertTableFieldToSortField(tableField, sortDirection);
            querySortFields.add(querySortField);
        }

        return querySortFields;
    }

    private TableField getTableField(String sortFieldName) {
        TableField sortField = null;
        try {
            Field tableField = TODOS.getClass().getField(sortFieldName);
            sortField = (TableField) tableField.get(TODOS);
        } catch (NoSuchFieldException | IllegalAccessException ex) {
            String errorMessage = String.format("Could not find table field: {}", sortFieldName);
            throw new InvalidDataAccessApiUsageException(errorMessage, ex);
        }

        return sortField;
    }

    private SortField<?> convertTableFieldToSortField(TableField tableField, Sort.Direction sortDirection) {
        if (sortDirection == Sort.Direction.ASC) {
            return tableField.asc();
        }
        else {
            return tableField.desc();
        }
    }

    private List<Todo> convertQueryResultsToModelObjects(List<TodosRecord> queryResults) {
        List<Todo> todoEntries = new ArrayList<>();

        for (TodosRecord queryResult : queryResults) {
            Todo todoEntry = convertQueryResultToModelObject(queryResult);
            todoEntries.add(todoEntry);
        }

        return todoEntries;
    }

    private Todo convertQueryResultToModelObject(TodosRecord queryResult) {
        return Todo.getBuilder(queryResult.getTitle())
                .creationTime(queryResult.getCreationTime())
                .description(queryResult.getDescription())
                .id(queryResult.getId())
                .modificationTime(queryResult.getModificationTime())
                .build();
    }
	
	//The other methods are omitted for the sake of clarity
}

Wir haben unserer Suchabfrage jetzt eine Sortierunterstützung hinzugefügt. Fahren wir fort und beenden unsere Suchfunktion, indem wir findBySearchTerm() Paginierungsunterstützung hinzufügen Methode.

Paginieren der Abfrageergebnisse

Wir können die Abfrageergebnisse unserer Suchanfrage paginieren, indem wir das LIMIT .. OFFSET hinzufügen -Klausel zu unserer Datenbankabfrage. Wir können dies tun, indem wir die folgenden Änderungen an der Implementierung unserer Datenbankabfrage vornehmen:

  1. Geben Sie die Anzahl der zurückgegebenen Zeilen an, indem Sie limit(int NumberOfRows) aufrufen Methode des SelectLimitStep -Schnittstelle und übergeben Sie die Seitengröße an einen Methodenparameter (Sie können die Seitengröße abrufen, indem Sie getPageSize() aufrufen Methode des Pageable Schnittstelle).
  2. Geben Sie den Offset an, indem Sie offset(int offset) aufrufen Methode des SelectOffsetStep -Schnittstelle und übergeben Sie den Offset als Methodenparameter (Sie können den Offset erhalten, indem Sie getOffset() aufrufen Methode des Pageable Schnittstelle).
  3. Fügen Sie einen privaten langen findCountByLikeExpression(String likeExpression) hinzu -Methode zur Repository-Klasse. Implementieren Sie diese Methode, indem Sie die folgenden Schritte ausführen:
    1. Ermitteln Sie die Anzahl der Aufgabeneinträge, indem Sie die Abfrage fetchCount(Select query) aufrufen Methode des DSLContext Klasse und übergeben Sie das verwendete SELECT Abfrage als Methodenparameter.
    2. Gib die Anzahl der Aufgabeneinträge zurück.
  4. Erhalten Sie die Anzahl der ToDo-Einträge, die mit dem gegebenen Like-Ausdruck übereinstimmen, indem Sie das private findCountByLikeExpression() aufrufen -Methode in findBySearchTerm() Methode. Übergeben Sie den verwendeten Like-Ausdruck als Methodenparameter.
  5. Erstellen Sie eine neue PageImpl Objekt und übergeben Sie die folgenden Informationen als Konstruktorargumente:
    1. Die Liste, die die gefundenen Aufgabeneinträge enthält.
    2. Das Pageable Objekt als Methodenparameter angegeben.
    3. Die Anzahl der ToDo-Einträge, die mit dem gegebenen Like-Ausdruck übereinstimmen.
  6. Gib die erstellte PageImpl zurück Objekt.

Nachdem wir diese Änderungen an unserer Repository-Methode vorgenommen haben, sieht der Quellcode unserer Repository-Methode wie folgt aus (die Änderungen sind hervorgehoben):

import org.jooq.DSLContext;
import org.jooq.SortField;
import org.jooq.TableField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import static net.petrikainulainen.spring.jooq.todo.db.tables.Todos.TODOS;

@Repository
public class JOOQTodoRepository implements TodoRepository {

    private final DateTimeService dateTimeService;

    private final DSLContext jooq;

	//The constructor is omitted for the sake of clarity

    @Transactional(readOnly = true)
    @Override
    public Page<Todo> findBySearchTerm(String searchTerm, Pageable pageable) {
        String likeExpression = "%" + searchTerm + "%";

        List<TodosRecord> queryResults = jooq.selectFrom(TODOS)
                .where(
                        TODOS.DESCRIPTION.likeIgnoreCase(likeExpression)
                                .or(TODOS.TITLE.likeIgnoreCase(likeExpression))
                )
                .orderBy(getSortFields(pageable.getSort()))
                .limit(pageable.getPageSize()).offset(pageable.getOffset())
                .fetchInto(TodosRecord.class);

	    List<Todo> todoEntries = convertQueryResultsToModelObjects(queryResults);
        long totalCount = findCountByLikeExpression(likeExpression);

		return new PageImpl<>(todoEntries, pageable, totalCount);
    }
	
    private long findCountByLikeExpression(String likeExpression) {
           return jooq.fetchCount(jooq.select()
		   			.from(TODOS)
					.where(
							TODOS.DESCRIPTION.likeIgnoreCase(likeExpression)
									.or(TODOS.TITLE.likeIgnoreCase(likeExpression))
					)
           );
	}
	
    private Collection<SortField<?>> getSortFields(Sort sortSpecification) {
        Collection<SortField<?>> querySortFields = new ArrayList<>();

        if (sortSpecification == null) {
            return querySortFields;
        }

        Iterator<Sort.Order> specifiedFields = sortSpecification.iterator();

        while (specifiedFields.hasNext()) {
            Sort.Order specifiedField = specifiedFields.next();

            String sortFieldName = specifiedField.getProperty();
            Sort.Direction sortDirection = specifiedField.getDirection();

            TableField tableField = getTableField(sortFieldName);
            SortField<?> querySortField = convertTableFieldToSortField(tableField, sortDirection);
            querySortFields.add(querySortField);
        }

        return querySortFields;
    }

    private TableField getTableField(String sortFieldName) {
        TableField sortField = null;
        try {
            Field tableField = TODOS.getClass().getField(sortFieldName);
            sortField = (TableField) tableField.get(TODOS);
        } catch (NoSuchFieldException | IllegalAccessException ex) {
            String errorMessage = String.format("Could not find table field: {}", sortFieldName);
            throw new InvalidDataAccessApiUsageException(errorMessage, ex);
        }

        return sortField;
    }

    private SortField<?> convertTableFieldToSortField(TableField tableField, Sort.Direction sortDirection) {
        if (sortDirection == Sort.Direction.ASC) {
            return tableField.asc();
        }
        else {
            return tableField.desc();
        }
    }

    private List<Todo> convertQueryResultsToModelObjects(List<TodosRecord> queryResults) {
        List<Todo> todoEntries = new ArrayList<>();

        for (TodosRecord queryResult : queryResults) {
            Todo todoEntry = convertQueryResultToModelObject(queryResult);
            todoEntries.add(todoEntry);
        }

        return todoEntries;
    }

    private Todo convertQueryResultToModelObject(TodosRecord queryResult) {
        return Todo.getBuilder(queryResult.getTitle())
                .creationTime(queryResult.getCreationTime())
                .description(queryResult.getDescription())
                .id(queryResult.getId())
                .modificationTime(queryResult.getModificationTime())
                .build();
    }
	
	//Other methods are omitted for the sake of clarity
}

Das ist alles, Leute. Fahren wir fort und fassen zusammen, was wir aus diesem Blogbeitrag gelernt haben.

Zusammenfassung

Wir haben jetzt eine Suchfunktion implementiert, die das Sortieren und Paginieren unterstützt. Dieses Tutorial hat uns vier Dinge beigebracht:

  • Wir haben gelernt, wie wir die Web-Paginierungsunterstützung des Spring Data Commons-Projekts nutzen können.
  • Wir haben gelernt, wie wir ORDER BY hinzufügen können -Klausel zu einer Datenbankabfrage.
  • Wir haben gelernt, wie wir das LIMIT .. OFFSET hinzufügen können -Klausel zu einer Datenbankabfrage.
  • Wir haben gelernt, wie wir eine neue Seite erstellen können Objekte, die unsere Abfrageergebnisse und nützliche Paginierungsmetadaten enthalten.

Der nächste Teil dieses Tutorials beschreibt, wie wir Spring Data JPA und jOOQ integrieren können und was noch wichtiger ist, warum wir das tun sollten.

P.S. Sie können die Beispielanwendung dieses Blogbeitrags von Github herunterladen.


Java-Tag