Рубрики
Без рубрики

Введение в Jinq с весной

Узнайте, как использовать библиотеку запросов к базе данных Jinq для Java 8 в приложении Spring.

Автор оригинала: baeldung.

1. введение

Jinq обеспечивает интуитивно понятный и удобный подход для запросов к базам данных на Java. В этом уроке мы рассмотрим как настроить проект Spring для использования Jing и некоторые его функции, проиллюстрированные простыми примерами.

2. Зависимости Maven

Нам нужно будет добавить зависимость соединения в pom.xml файл:


    org.jinq
    jinq-jpa
    1.8.22

Для Spring мы добавим зависимость Spring ORM в pom.xml файл:


    org.springframework
    spring-orm
    5.3.3

Наконец, для тестирования мы будем использовать базу данных H2 в памяти, поэтому давайте также добавим эту зависимость вместе с spring-boot-starter-data-jpa в pom.xml файл:


    com.h2database
    h2
    1.4.200


    org.springframework.boot
    spring-boot-starter-data-jpa
    2.4.0

3. Понимание Jinq

Junk помогает нам писать более простые и читаемые запросы к базе данных, предоставляя свободный API, который внутренне основан на API потока Java.

Давайте рассмотрим пример, где мы фильтруем автомобили по модели:

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

Jinq переводит приведенный выше фрагмент кода в SQL-запрос эффективным способом , поэтому конечным запросом в этом примере будет:

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

Поскольку мы не используем обычный текст для написания запросов и вместо этого используем типобезопасный API, этот подход менее подвержен ошибкам.

Кроме того, Jinq стремится обеспечить более быструю разработку, используя общие, легко читаемые выражения.

Тем не менее, он имеет некоторые ограничения в количестве типов и операций, которые мы можем использовать, как мы увидим далее.

3.1. Ограничения

Jinq поддерживает только основные типы в JPA и конкретный список функций SQL. Он работает путем перевода лямбда-операций в собственный SQL-запрос путем сопоставления всех объектов и методов в тип данных JPA и функцию SQL.

Поэтому мы не можем ожидать, что инструмент переведет каждый пользовательский тип или все методы типа.

3.2. Поддерживаемые Типы данных

Давайте рассмотрим поддерживаемые типы данных и поддерживаемые методы:

  • Stringequals() , compareTo() только методы
  • Примитивные типы данных – арифметические операции
  • Перечисления и пользовательские классы – поддерживает только
  • java.util.Коллекция – содержит()
  • Дата API – равно() , до() , после() только методы

Примечание: если бы мы хотели настроить преобразование из объекта Java в объект базы данных, нам нужно было бы зарегистрировать нашу конкретную реализацию Преобразователь атрибутов в Джинке.

4. Интеграция Jinq С Пружиной

Jinq нуждается в экземпляре EntityManager , чтобы получить контекст сохранения. В этом уроке мы представим простой подход с Spring, чтобы заставить Jinq работать с EntityManager , предоставляемым Hibernate .

4.1. Интерфейс репозитория

Spring использует концепцию репозиториев для управления сущностями. Давайте посмотрим на наш CarRepository интерфейс, где у нас есть метод получения Car для данной модели:

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

4.2. Репозиторий абстрактной базы

Далее, нам понадобится базовый репозиторий , чтобы предоставить все ненужные возможности:

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. Реализация репозитория

Теперь все, что нам нужно для Джинка, – это EntityManager экземпляр и класс типа сущности.

Давайте посмотрим реализацию репозитория Car , используя наш базовый репозиторий Jinq, который мы только что определили:

@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. Подключение устройства JinqJPAStreamProvider

Чтобы подключить экземпляр JinqJPAStreamProvider , мы добавим конфигурацию поставщика Jinq:

@Configuration
public class JinqProviderConfiguration {

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

4.5. Настройка приложения Spring

Последним шагом является настройка нашего приложения Spring с помощью Hibernate и нашей конфигурации Jinq. В качестве ссылки см. наш файл application.properties , в котором мы используем экземпляр H2 в памяти в качестве базы данных:

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

5. Руководство по запросу

Jinq предоставляет множество интуитивно понятных опций для настройки конечного SQL-запроса с помощью select, where, |/joins и многого другого. Обратите внимание, что они имеют

5.1. Где

Предложение where позволяет применять несколько фильтров к сбору данных.

В следующем примере мы хотим отфильтровать автомобили по модели и описанию:

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

И это SQL, который переводит Jinq:

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

5.2. Выберите

В случае, если мы хотим получить только несколько столбцов/полей из базы данных, нам нужно использовать предложение select .

Чтобы сопоставить несколько значений, Jinq предоставляет несколько классов Tuple , содержащих до восьми значений:

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

И переведенный SQL:

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

5.3. Присоединяется

Jinq способен разрешать отношения “один к одному” и “много к одному” , если сущности правильно связаны.

Например, если мы добавим сущность производителя в Car :

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

И Производитель юридическое лицо со списком Автомобилей ов:

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

И Производитель юридическое лицо со списком

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

Как и ожидалось, Jinq будет использовать внутреннее предложение SQL join в этом сценарии:

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

На случай, если нам понадобится больше контроля над присоединиться предложения для реализации более сложных отношений над сущностями, таких как отношение “многие ко многим”, мы можем использовать присоединиться метод:

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

Наконец, мы могли бы использовать SQL-предложение left outer join, используя метод left Outer Join вместо метода join .

5.4. Агрегации

Все примеры, которые мы представили до сих пор, используют методы ToList или findFirst – для возврата конечного результата нашего запроса в Jinq.

Помимо этих методов, у нас также есть доступ к другим методам для агрегирования результатов .

Например, давайте используем метод count , чтобы получить общее количество автомобилей для конкретной модели в нашей базе данных:

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

И окончательный SQL использует метод count SQL, как и ожидалось:

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

Jinq также предоставляет методы агрегирования, такие как sum , average , min , max, и возможность комбинировать различные агрегации .

5.5. Разбиение на страницы

В случае, если мы хотим читать данные пакетами, мы можем использовать методы limit и skip .

Давайте рассмотрим пример, где мы хотим пропустить первые 10 автомобилей и получить только 20 предметов:

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

И сгенерированный SQL:

select c.* from car c limit ? offset ?

6. Заключение

Вот и все. В этой статье мы рассмотрели подход к настройке

Мы также кратко рассмотрели преимущества Jinq и некоторые из его основных функций.

Как всегда, источники можно найти на GitHub .