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

Spring from the Trenches:Neue ähnliche Ausdrücke von Spring Data JPA

Die heutige Kriegsgeschichte spricht über die Behandlung ähnlicher Ausdrücke von Spring Data JPA.

Obwohl ich zuvor über eine bessere Lösung für die Implementierung textbasierter Suchfunktionen geschrieben habe, ist die Verwendung eines externen Suchservers wie Solr keine praktikable Option, wenn die implementierte Suchfunktion ziemlich einfach ist.

Betrachten wir das folgende Beispiel.

Ähnliche Verwendung der Like-Ausdrücke

Wir haben eine einfache Entität namens Person die zwei Felder hat:id und Nachname . Der Quellcode dieser Entität sieht wie folgt aus:

import javax.persistence.*;

@Entity
@Table(name = "persons")
public class Person {
   
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
   
    @Column(name = "last_name", nullable = false)
    private String lastName;
   
	//Methods are omitted.
}

Nehmen wir an, wir müssen eine Suchfunktion implementieren, die Personen zurückgibt, deren Nachname mit dem gegebenen Like-Ausdruck übereinstimmt. Da diese Aufgabe offensichtlich so anspruchsvoll ist, müssen wir die Suchfunktion implementieren, indem wir sowohl SQL- als auch JPQL-Abfragen verwenden. Die in der Produktionsversion unserer Anwendung verwendete Abfragemethode wird entschieden, nachdem wir herausgefunden haben, welche Implementierung schneller ist.

Nachdem wir die beiden Abfragemethoden implementiert haben, sieht der Quellcode unserer Repository-Schnittstelle wie folgt aus:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface PersonRepository extends JpaRepository<Person, Long> {

    @Query(
            value = "SELECT * FROM persons WHERE last_name LIKE :searchTerm",
            nativeQuery = true
    )
    public List<Person> searchWithNativeQuery(@Param("searchTerm") String searchTerm);

    @Query("SELECT p FROM Person p WHERE p.lastName LIKE :searchTerm")
    public List<Person> searchWithJPQLQuery(@Param("searchTerm") String searchTerm);

}

Das Problem bei diesem Ansatz besteht darin, dass wir den ähnlichen Ausdruck auf der Dienstschicht erstellen müssen. Der Quellcode unserer Dienstimplementierung sieht wie folgt aus:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class RepositoryPersonService implements PersonService {

	private PersonRepository repository;
	
	@Autowired
	public RepositoryPersonService(PersonRepository repository) {
		this.repository = repository;
	}
	
	@Transactional(readOnly=true)
	@Override
	public List<Person> search(String searchTerm) {
		String likeExpression = "%" + searchTerm + "%";
		//Call the correct query method, pass the like expression as method parameter
		//and return the found persons.
	}
}

Obwohl ich dies nicht als großes Problem bezeichnen würde, gehört die Erstellung des Like-Ausdrucks nicht zu den Aufgaben der Serviceschicht. Natürlich könnten wir dieses Problem lösen, indem wir unserem Repository eine benutzerdefinierte Methode hinzufügen, aber das wäre etwas extrem.

Glücklicherweise bietet Spring Data JPA eine bessere Lösung dafür. Lassen Sie uns herausfinden, was es ist.

Eine neue und bessere Welt

Spring Data JPA Version 1.3.1 fügte eine Möglichkeit hinzu, ähnliche Ausdrücke "innerhalb" der ausgeführten Abfrage anzugeben. Wir können unser Beispiel ändern, um diese Strategie zu verwenden, indem wir die folgenden Schritte ausführen:

  1. Ändern Sie die Abfragemethode und geben Sie ähnliche Ausdrücke "innerhalb" der Abfragen an.
  2. Bereinigen Sie den RepositoryPersonService Klasse, indem Sie die Logik zur Erstellung ähnlicher Ausdrücke daraus entfernen.

Lassen Sie uns weitermachen und herausfinden, wie wir die Like-Ausdruckserstellungslogik von der Dienstschicht zu unserer Repository-Schnittstelle verschieben.

Ändern der Abfragemethoden

Da wir Personen finden möchten, deren Nachname den angegebenen Suchbegriff enthält, müssen wir die Abfragemethoden unseres Repositorys ändern, indem wir das Zeichen '%' am Anfang und am Ende des Platzhalters hinzufügen (:Suchbegriff ), der beim Erstellen der ausgeführten Abfrage durch den angegebenen Suchbegriff ersetzt wird.

Der Quellcode des PersonRepository Die Benutzeroberfläche sieht wie folgt aus:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface PersonRepository extends JpaRepository<Person, Long> {

    @Query(
            value = "SELECT * FROM persons WHERE last_name LIKE %:searchTerm%",
            nativeQuery = true
    )
    public List<Person> searchWithNativeQuery(@Param("searchTerm") String searchTerm);

    @Query("SELECT p FROM Person p WHERE p.lastName LIKE %:searchTerm%")
    public List<Person> searchWithJPQLQuery(@Param("searchTerm") String searchTerm);

}

Hinweis :Wir können natürlich auch andere ähnliche Ausdrücke verwenden:

  • Wenn wir Personen finden wollen, deren Nachname mit dem angegebenen Suchbegriff beginnt, müssen wir am Ende des Platzhalters das Zeichen '%' hinzufügen.
  • Wir können die Personen finden, deren Nachname mit dem angegebenen Suchbegriff endet, indem wir das Zeichen '%' am Anfang des Platzhalters hinzufügen.

Genug der Theorie. Werfen wir einen kurzen Blick auf unsere neue Serviceklasse.

Aufräumen der Serviceklasse

Wir können jetzt die Logik zur Erstellung ähnlicher Ausdrücke aus unserer Serviceklasse entfernen. Der Quellcode des RepositoryPersonService Klasse sieht wie folgt aus:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class RepositoryPersonService implements PersonService {

	private PersonRepository repository;
	
	@Autowired
	public RepositoryPersonService(PersonRepository repository) {
		this.repository = repository;
	}
	
	@Transactional(readOnly=true)
	@Override
	public List<Person> search(String searchTerm) {
		//Call the correct query method and and return the results.
	}
}

Wir sind fertig. Lassen Sie uns einen Moment damit verbringen, zusammenzufassen, was wir gelernt haben.

Zusammenfassung

Dieser Blogpost hat uns zwei Dinge gelehrt:

  • Wir können einen ähnlichen Ausdruck "innerhalb" der ausgeführten Abfrage angeben, wenn wir Spring Data JPA 1.3.1 oder neuer verwenden.
  • Wir können diesen Ansatz sowohl für native als auch für JPQL-Abfragen verwenden.

Wie ich bereits sagte, mag dies wie eine geringfügige Verbesserung erscheinen, aber wir müssen bedenken, dass eine saubere Codebasis durch viele kleine Verbesserungen aufgebaut wird.

Ein Wort der Warnung. Spring Data JPA 1.3.2 hat einen Fehler eingeführt, der die Abwärtskompatibilität der Behandlung ähnlicher Ausdrücke unterbricht. Das bedeutet, dass wir, wenn wir Spring Data JPA 1.3.2 verwenden möchten, unsere Abfragen aktualisieren müssen, indem wir den in diesem Blogbeitrag beschriebenen Ansatz verwenden.


Java-Tag