Введение в Jinq с помощью Spring

Введение в Jinq с весны

1. Вступление

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

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

Нам нужно будет добавитьthe Jinq dependency в файлpom.xml:


    org.jinq
    jinq-jpa
    1.8.22

Для Spring мы добавимthe Spring ORM dependency в файлpom.xml:


    org.springframework
    spring-orm
    5.0.3.RELEASE

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


    com.h2database
    h2
    1.4.196

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

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

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

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, поэтому последний запрос в этом примере будет:

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

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

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

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

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

Jinq supports only the basic types in JPA and a concrete list of SQL functions. Он работает, переводя лямбда-операции в собственный SQL-запрос, отображая все объекты и методы в тип данных JPA и функцию SQL.

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

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

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

  • String - только методыequals(),compareTo()

  • Примитивные типы данных - арифметические операции

  • Enums и пользовательские классы - поддерживает только операции == и! =

  • java.util.Collection – содержит()

  • Date API - только методыequals(),before(),after()

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

4. Интеграция Jinq со Spring

Jinq needs an EntityManager instance to get the persistence context. В этом руководстве мы познакомимся с простым подходом со Spring, чтобы заставить Jinq работать сEntityManager, предоставляемымHibernate.

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

Spring uses the concept of repositories to manage entities. Давайте посмотрим на наш интерфейсCarRepository, где у нас есть метод для полученияCar для данной модели:

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

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

Затемwe’ll need a base repository, чтобы предоставить все возможности Jinq:

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

Теперь все, что нам нужно для Jinq, - это экземпляр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, мы используемadd the Jinq provider configuration:

@Configuration
public class JinqProviderConfiguration {

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

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

Последний шаг -configure our Spring application using Hibernate and our Jinq configuration.. В качестве справки просмотрите наш файл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 provides many intuitive options to customize the final SQL query with select, where, joins and more. Обратите внимание, что они имеют те же ограничения, которые мы уже ввели выше.

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 is able to resolve one-to-one and many-to-one relationships, если объекты правильно связаны.

Например, если мы добавим объект производителя вCar:

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

И объектManufacturer со спискомCars:

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

Теперь мы можем получитьManufacturer для данной модели:

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

Как и ожидалось,Jinq will use an inner join SQL clause в этом сценарии:

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

В случае, если нам нужно иметь больший контроль над предложениямиjoin, чтобы реализовать более сложные отношения между сущностями, например, отношение «многие ко многим», мы можем использовать методjoin:

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

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

5.4. Скопления

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

Помимо этих методов,we also have access to other methods to aggregate results.

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

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

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

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

Jinq также предоставляет такие методы агрегирования, какsum,average,min,max, иpossibility to combine different aggregations.

5.5. пагинация

Если мы хотим читать данные партиями, мы можем использовать методыlimit иskip.

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

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

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

select c.* from car c limit ? offset ?

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

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

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

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