Einführung in Jinq mit Frühling

Einführung in Jinq mit Spring

1. Einführung

Jinq bietet einen intuitiven und praktischen Ansatz zum Abfragen von Datenbanken in Java. In diesem Tutorial werden wirhow to configure a Spring project to use Jinq und einige seiner Funktionen anhand einfacher Beispiele untersuchen.

2. Maven-Abhängigkeiten

Wir müssenthe Jinq dependency in diepom.xml-Datei einfügen:


    org.jinq
    jinq-jpa
    1.8.22

Für Spring fügen wirthe Spring ORM dependency in diepom.xml-Datei ein:


    org.springframework
    spring-orm
    5.0.3.RELEASE

Schließlich verwenden wir zum Testen eine H2-In-Memory-Datenbank. Fügen wir also auchthis dependency zurpom.xml-Datei hinzu:


    com.h2database
    h2
    1.4.196

3. Jinq verstehen

Jinq hilft uns dabei, einfachere und besser lesbare Datenbankabfragen zu schreiben, indem eine fließende API verfügbar gemacht wird, die intern aufthe Java Stream API. basiert

Sehen wir uns ein Beispiel an, in dem wir Autos nach Modell filtern:

jinqDataProvider.streamAll(entityManager, Car.class)
  .where(c -> c.getModel().equals(model))
  .toList();

Jinq translates the above code snippet into a SQL query in an efficient way, daher wäre die letzte Abfrage in diesem Beispiel:

select c.* from car c where c.model=?

Da wir zum Schreiben von Abfragen keinen Klartext verwenden und stattdessen eine typsichere API verwenden, ist dieser Ansatz weniger fehleranfällig.

Darüber hinaus ist Jinq bestrebt, eine schnellere Entwicklung durch die Verwendung allgemeiner, einfach zu lesender Ausdrücke zu ermöglichen.

Es gibt jedoch einige Einschränkungen hinsichtlich der Anzahl der Typen und Operationen, die wir verwenden können, wie wir als Nächstes sehen werden.

3.1. Einschränkungen

Jinq supports only the basic types in JPA and a concrete list of SQL functions. Es funktioniert, indem die Lambda-Operationen in eine native SQL-Abfrage übersetzt werden, indem alle Objekte und Methoden einem JPA-Datentyp und einer SQL-Funktion zugeordnet werden.

Daher können wir nicht erwarten, dass das Tool jeden benutzerdefinierten Typ oder alle Methoden eines Typs übersetzt.

3.2. Unterstützte Datentypen

Sehen wir uns die unterstützten Datentypen und unterstützten Methoden an:

  • String -equals(),compareTo() nur Methoden

  • Primitive Datentypen - arithmetische Operationen

  • Enums und benutzerdefinierte Klassen - unterstützt nur Operationen == und! =

  • java.util.Collection – enthält ()

  • Date API - Nurequals(),before(),after() Methoden

Hinweis: Wenn wir die Konvertierung von einem Java-Objekt in ein Datenbankobjekt anpassen möchten, müssen wir unsere konkrete Implementierung vonAttributeConverter in Jinq registrieren.

4. Jinq in Spring integrieren

Jinq needs an EntityManager instance to get the persistence context. In diesem Tutorial stellen wir Spring einen einfachen Ansatz vor, mit dem Jinq mit den vonHibernate bereitgestelltenEntityManager funktioniert.

4.1. Repository-Schnittstelle

Spring uses the concept of repositories to manage entities. Schauen wir uns unsereCarRepository-Schnittstelle an, auf der wir eine Methode zum Abrufen vonCar für ein bestimmtes Modell haben:

public interface CarRepository {
    Optional findByModel(String model);
}

4.2. Abstraktes Basis-Repository

Als nächsteswe’ll need a base repository, um alle Jinq-Funktionen bereitzustellen:

public abstract class BaseJinqRepositoryImpl {
    @Autowired
    private JinqJPAStreamProvider jinqDataProvider;

    @PersistenceContext
    private EntityManager entityManager;

    protected abstract Class entityType();

    public JPAJinqStream stream() {
        return streamOf(entityType());
    }

    protected  JPAJinqStream streamOf(Class clazz) {
        return jinqDataProvider.streamAll(entityManager, clazz);
    }
}

4.3. Repository implementieren

Jetzt brauchen wir für Jinq nur noch eineEntityManager-Instanz und die Entitätstypklasse.

Sehen wir uns die Repository-Implementierung vonCarmit unserem soeben definierten Jinq-Basis-Repository an:

@Repository
public class CarRepositoryImpl
  extends BaseJinqRepositoryImpl implements CarRepository {

    @Override
    public Optional findByModel(String model) {
        return stream()
          .where(c -> c.getModel().equals(model))
          .findFirst();
    }

    @Override
    protected Class entityType() {
        return Car.class;
    }
}

4.4. Verdrahtung derJinqJPAStreamProvider

Um die Instanz vonJinqJPAStreamProviderzu verkabeln, werden wiradd the Jinq provider configuration: verwenden

@Configuration
public class JinqProviderConfiguration {

    @Bean
    @Autowired
    JinqJPAStreamProvider jinqProvider(EntityManagerFactory emf) {
        return new JinqJPAStreamProvider(emf);
    }
}

4.5. Konfigurieren der Spring-Anwendung

Der letzte Schritt istconfigure our Spring application using Hibernate and our Jinq configuration.. Als Referenz sehen Sie unsereapplication.properties-Datei, in der wir eine speicherinterne H2-Instanz als Datenbank verwenden:

spring.datasource.url=jdbc:h2:~/jinq
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop

5. Abfragehandbuch

Jinq provides many intuitive options to customize the final SQL query with select, where, joins and more. Beachten Sie, dass diese dieselben Einschränkungen haben, die wir oben bereits eingeführt haben.

5.1. Wo

Diewhere-Klausel ermöglicht das Anwenden mehrerer Filter auf eine Datenerfassung.

Im nächsten Beispiel möchten wir Autos nach Modell und Beschreibung filtern:

stream()
  .where(c -> c.getModel().equals(model)
    && c.getDescription().contains(desc))
  .toList();

Und dies ist die SQL, die Jinq übersetzt:

select c.model, c.description from car c where c.model=? and locate(?, c.description)>0

5.2. Wählen

Wenn wir nur einige Spalten / Felder aus der Datenbank abrufen möchten, müssen wir dieselect-Klausel verwenden.

Um mehrere Werte abzubilden, bietet Jinq eine Reihe vonTuple-Klassen mit bis zu acht Werten:

stream()
  .select(c -> new Tuple3<>(c.getModel(), c.getYear(), c.getEngine()))
  .toList()

Und das übersetzte SQL:

select c.model, c.year, c.engine from car c

5.3. Tritt bei

Jinq is able to resolve one-to-one and many-to-one relationships, wenn die Entitäten ordnungsgemäß verknüpft sind.

Wenn wir beispielsweise die Herstellerentität inCar hinzufügen:

@Entity(name = "CAR")
public class Car {
    //...
    @OneToOne
    @JoinColumn(name = "name")
    public Manufacturer getManufacturer() {
        return manufacturer;
    }
}

Und die EntitätManufacturer mit der Liste vonCars:

@Entity(name = "MANUFACTURER")
public class Manufacturer {
    // ...
    @OneToMany(mappedBy = "model")
    public List getCars() {
        return cars;
    }
}

Wir können jetzt dieManufacturer für ein bestimmtes Modell erhalten:

Optional manufacturer = stream()
  .where(c -> c.getModel().equals(model))
  .select(c -> c.getManufacturer())
  .findFirst();

Wie erwartet,Jinq will use an inner join SQL clause in diesem Szenario:

select m.name, m.city from car c inner join manufacturer m on c.name=m.name where c.model=?

Falls wir mehr Kontrolle über diejoin-Klauseln benötigen, um komplexere Beziehungen über die Entitäten zu implementieren, wie z. B. eine Viele-zu-Viele-Beziehung, können wir diejoin-Methode verwenden:

List> list = streamOf(Manufacturer.class)
  .join(m -> JinqStream.from(m.getCars()))
  .toList()

Schließlich könnten wir eine SQL-Klausel für den linken äußeren Join verwenden, indem wir die MethodeleftOuterJoinanstelle der Methodejoinverwenden.

5.4. Aggregationen

Alle Beispiele, die wir bisher vorgestellt haben, verwenden entweder die MethodentoList oderfindFirst, um das Endergebnis unserer Abfrage in Jinq zurückzugeben.

Neben diesen Methoden sindwe also have access to other methods to aggregate results.

Verwenden Sie beispielsweise die Methodecount, um die Gesamtzahl der Autos für ein konkretes Modell in unserer Datenbank abzurufen:

long total = stream()
  .where(c -> c.getModel().equals(model))
  .count()

Und das endgültige SQL verwendet erwartungsgemäß die SQL-Methodecount:

select count(c.model) from car c where c.model=?

Jinq bietet auch Aggregationsmethoden wiesum,average,min,max, undpossibility to combine different aggregations.

5.5. Seitennummerierung

Wenn wir Daten stapelweise lesen möchten, können wir die Methodenlimit undskip verwenden.

Sehen wir uns ein Beispiel an, in dem wir die ersten 10 Autos überspringen und nur 20 Artikel erhalten möchten:

stream()
  .skip(10)
  .limit(20)
  .toList()

Und das generierte SQL ist:

select c.* from car c limit ? offset ?

6. Fazit

Da gehen wir. In diesem Artikel haben wir einen Ansatz zum Einrichten einer Spring-Anwendung mit Jinq unter Verwendung von Hibernate (minimal) gesehen.

Wir haben auch kurz die Vorteile von Jinq und einige seiner Hauptmerkmale untersucht.

Wie immer können die Quellenover on GitHub gefunden werden.