Въведение в Jinq с пролетта

1. Въведение

Jinq предоставя интуитивен и удобен подход за заявки към бази данни в Java. В този урок ще разгледаме как да конфигурираме проект Spring за използване на Jinq и някои от неговите функции, илюстрирани с прости примери.

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

Ще трябва да добавим зависимостта Jinq във файла pom.xml :

 org.jinq jinq-jpa 1.8.22 

За Spring ще добавим зависимостта Spring ORM във файла pom.xml :

 org.springframework spring-orm 5.2.5.RELEASE 

И накрая, за тестване ще използваме база данни H2 в паметта, така че нека също добавим тази зависимост, заедно с spring-boot-starter-data-jpa към файла pom.xml:

 com.h2database h2 1.4.200   org.springframework.boot spring-boot-starter-data-jpa 2.2.6.RELEASE 

3. Разбиране на Jinq

Jinq ни помага да пишем по-лесни и по-четливи заявки към база данни, като излагаме плавен API, който се базира вътрешно на Java Stream API.

Нека видим пример, където филтрираме колите по модели:

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. Поддържани типове данни

Нека видим поддържаните поддържани типове данни и методи:

  • String - е равно на () , само методите compareTo ()
  • Примитивни типове данни - аритметични операции
  • Преброяване и персонализирани класове - поддържа само операции == и! =
  • java.util.Collection - съдържа ()
  • Дата API - равни () , преди () , след () методи само

Забележка: ако искахме да персонализираме преобразуването от Java обект в обект на база данни, ще трябва да регистрираме конкретната ни реализация на AttributeConverter в Jinq.

4. Интегриране на Jinq с пролетта

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

4.1. Интерфейс на хранилището

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

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

4.2. Хранилище за абстрактни бази

След това ще се нуждаем от основно хранилище, за да предоставим всички възможности на 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 , ще добавим конфигурацията на доставчика на 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, присъединявания и други. Имайте предвид, че те имат същите ограничения, които вече въведохме по-горе.

5.1. Където

Най където клауза позволява прилагането на множество филтри за събиране на данни.

В следващия пример искаме да филтрираме колите по модел и описание:

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. Изберете

In case we want to retrieve only a few columns/fields from the database, we need to use the select clause.

In order to map multiple values, Jinq provides a number of Tuple classes with up to eight values:

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

And the translated SQL:

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

5.3. Joins

Jinq is able to resolve one-to-one and many-to-one relationships if the entities are properly linked.

For example, if we add the manufacturer entity in Car:

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

And the Manufacturer entity with the list of Cars:

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

We're now able to get the Manufacturer for a given model:

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

As expected, Jinq will use an inner join SQL clause in this scenario:

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

In case we need to have more control over the join clauses in order to implement more complex relationships over the entities, like a many-to-many relation, we can use the join method:

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

Finally, we could use a left outer join SQL clause by using the leftOuterJoin method instead of the join method.

5.4. Aggregations

All the examples we have introduced so far are using either the toList or the findFirst methods – to return the final result of our query in Jinq.

Besides these methods, we also have access to other methods to aggregate results.

For example, let's use the count method to get the total count of the cars for a concrete model in our database:

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

And the final SQL is using the count SQL method as expected:

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

Jinq also provides aggregation methods like sum, average, min, max, and the possibility to combine different aggregations.

5.5. Pagination

In case we want to read data in batches, we can use the limit and skip methods.

Let's see an example where we want to skip the first 10 cars and get only 20 items:

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

And the generated SQL is:

select c.* from car c limit ? offset ?

6. Conclusion

Ето. В тази статия видяхме подход за настройване на пролетно приложение с Jinq, използващо Hibernate (минимално).

Също така накратко разгледахме предимствата на Jinq и някои от основните му характеристики.

Както винаги, източниците могат да бъдат намерени в GitHub.