1. 소개
Jinq 는 Java로 데이터베이스를 쿼리하는 직관적이고 편리한 접근 방식을 제공합니다. 이 예제에서는 Jinq를 사용하도록 Spring 프로젝트를 구성하는 방법 과 간단한 예제로 설명 된 일부 기능을 탐색 합니다 .
2. Maven 의존성
pom.xml 파일 에 Jinq 의존성 을 추가해야 합니다.
<dependency>
<groupId>org.jinq</groupId>
<artifactId>jinq-jpa</artifactId>
<version>1.8.22</version>
</dependency>
Spring의 경우 pom.xml 파일 에 Spring ORM 의존성 을 추가 합니다.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.3</version>
</dependency>
마지막으로 테스트를 위해 H2 인 메모리 데이터베이스를 사용하므로 spring-boot-starter-data-jpa 와 함께이 의존성 을 pom.xml 파일에 추가해 보겠습니다 .
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.0</version>
</dependency>
3. Jinq 이해
Jinq는 내부적으로 Java Stream API를 기반으로하는 유창한 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 함수 List 만 지원합니다. 모든 개체와 메서드를 JPA 데이터 형식과 SQL 함수로 매핑하여 람다 작업을 네이티브 SQL 쿼리로 변환하여 작동합니다.
따라서 도구가 모든 사용자 지정 형식 또는 형식의 모든 메서드를 번역 할 것으로 기대할 수 없습니다.
3.2. 지원되는 데이터 유형
지원되는 데이터 유형 및 지원 방법을 살펴 보겠습니다.
- String – equals () , compareTo () 메서드 만
- 원시 데이터 유형 – 산술 연산
- 열거 형 및 사용자 지정 클래스 – == 및! = 작업 만 지원
- java.util.Collection – contains ()
- 날짜 API – equals () , before () , after () 메서드 만
참고 : Java 개체에서 데이터베이스 개체로의 변환을 사용자 지정하려면 Jinq에 AttributeConverter 의 구체적인 구현을 등록해야합니다 .
4. Jinq와 Spring의 통합
Jinq 는 지속성 컨텍스트를 가져 오기 위해 EntityManager 인스턴스가 필요합니다 . 이 예제에서는 Hibernate에서 제공 하는 EntityManager 와 함께 Jinq가 작동하도록 Spring을 사용한 간단한 접근 방식을 소개합니다 .
4.1. 리포지토리 인터페이스
Spring은 저장소 개념을 사용하여 엔티티를 관리합니다. 주어진 모델에 대해 Car 를 검색하는 메서드가있는 CarRepository 인터페이스를 살펴 보겠습니다 .
public interface CarRepository {
Optional<Car> findByModel(String model);
}
4.2. 추상 기본 저장소
다음으로 모든 Jinq 기능을 제공하기위한 기본 저장소 가 필요 합니다.
public abstract class BaseJinqRepositoryImpl<T> {
@Autowired
private JinqJPAStreamProvider jinqDataProvider;
@PersistenceContext
private EntityManager entityManager;
protected abstract Class<T> entityType();
public JPAJinqStream<T> stream() {
return streamOf(entityType());
}
protected <U> JPAJinqStream<U> streamOf(Class<U> clazz) {
return jinqDataProvider.streamAll(entityManager, clazz);
}
}
4.3. 리포지토리 구현
이제 Jinq에 필요한 것은 EntityManager 인스턴스와 엔티티 유형 클래스 뿐입니다 .
방금 정의한 Jinq 기본 저장소를 사용하여 Car 저장소 구현을 살펴 보겠습니다 .
@Repository
public class CarRepositoryImpl
extends BaseJinqRepositoryImpl<Car> implements CarRepository {
@Override
public Optional<Car> findByModel(String model) {
return stream()
.where(c -> c.getModel().equals(model))
.findFirst();
}
@Override
protected Class<Car> 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 애플리케이션 구성
마지막 단계는 Hibernate와 Jinq 구성을 사용하여 Spring 애플리케이션을 구성하는 것입니다. 참고로 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는 select, where, joins 등으로 최종 SQL 쿼리를 사용자 정의 할 수있는 많은 직관적 인 옵션을 제공합니다 . 위에서 소개 한 것과 동일한 제한 사항이 있습니다.
5.1. 어디
곳의 절은 데이터 수집에 여러 필터를 적용 할 수 있습니다.
다음 예에서는 모델 및 설명별로 자동차를 필터링하려고합니다.
stream()
.where(c -> c.getModel().equals(model)
&& c.getDescription().contains(desc))
.toList();
그리고 이것은 Jinq가 번역하는 SQL입니다.
select c.model, c.description from car c where c.model=? and locate(?, c.description)>0
5.2. 고르다
데이터베이스에서 몇 개의 열 / 필드 만 검색하려면 select 절 을 사용해야합니다 .
여러 값을 매핑하기 위해 Jinq는 최대 8 개의 값이 포함 된 여러 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는 엔터티가 제대로 연결되어있는 경우 일대일 및 다 대일 관계를 해결할 수 있습니다.
예를 들어, 우리가 제조 업체 엔티티를 추가하면 자동차 :
@Entity(name = "CAR")
public class Car {
//...
@OneToOne
@JoinColumn(name = "name")
public Manufacturer getManufacturer() {
return manufacturer;
}
}
Car s List이 있는 Manufacturer 엔티티 :
@Entity(name = "MANUFACTURER")
public class Manufacturer {
// ...
@OneToMany(mappedBy = "model")
public List<Car> getCars() {
return cars;
}
}
이제 주어진 모델에 대한 제조업체 를 가져올 수 있습니다.
Optional<Manufacturer> manufacturer = stream()
.where(c -> c.getModel().equals(model))
.select(c -> c.getManufacturer())
.findFirst();
예상대로 Jinq는 이 시나리오에서 내부 조인 SQL 절 을 사용합니다 .
select m.name, m.city from car c inner join manufacturer m on c.name=m.name where c.model=?
다 대다 관계와 같이 엔터티에 대해 더 복잡한 관계를 구현하기 위해 조인 절을 더 많이 제어해야하는 경우 조인 메서드를 사용할 수 있습니다 .
List<Pair<Manufacturer, Car>> list = streamOf(Manufacturer.class)
.join(m -> JinqStream.from(m.getCars()))
.toList()
마지막으로, 우리는 왼쪽 외부가 사용하여 SQL 절을 조인을 사용 할 수 leftOuterJoin의 (가) 대신 방법 가입 방법.
5.4. 집계
지금까지 소개 한 모든 예제는 Jinq 에서 쿼리의 최종 결과를 반환하기 위해 toList 또는 findFirst 메서드를 사용하고 있습니다.
이러한 방법 외에도 결과를 집계하기 위해 다른 방법에 액세스 할 수도 있습니다 .
예를 들어, 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. 결론
우리는 거기에 갈. 이 기사에서 우리는 (최소한) Hibernate를 사용하여 Jinq로 Spring 애플리케이션을 설정하는 접근 방식을 보았다.
또한 Jinq의 장점과 주요 기능 중 일부를 간략하게 살펴 보았습니다.
항상 그렇듯이 소스는 GitHub 에서 찾을 수 있습니다 .