Streaming Query Results, Asynchronous Query Results

2024. 10. 20. 11:47Spring Boot/Spring Data JPA

Streaming Query Results

Streaming Query Results는 데이터베이스 쿼리 결과를 Java 8의 Stream<T> 타입으로 반환하고, 이를 통해 쿼리 결과를 점진적으로 처리할 수 있는 방법입니다. 이 방식은 쿼리 결과를 Stream으로 감싸는 대신, 데이터 저장소에 특화된 메서드를 사용하여 스트리밍을 수행합니다.

Java 8 Stream<T>을 사용한 쿼리 결과 스트리밍 예시

@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);

위 예시에서는 세 가지 방법으로 Stream<User>를 반환하는 쿼리 메서드를 정의하고 있습니다. 각 메서드는 Stream<T>를 사용하여 쿼리 결과를 처리할 수 있습니다.

중요한 점: Stream<T> 사용 시 리소스 관리

Stream은 기본적으로 하위 데이터 저장소와 관련된 리소스를 감싸고 있을 수 있기 때문에, 사용이 끝난 후 반드시 Stream을 닫아야 합니다. 이를 위해 두 가지 방법을 사용할 수 있습니다:

  1. 수동으로 Stream 닫기: Streamclose() 메서드를 호출하여 수동으로 닫을 수 있습니다.
  2. Java 7의 try-with-resources 블록 사용: try-with-resources 블록을 사용하여 Stream이 자동으로 닫히도록 할 수 있습니다.

try-with-resources 블록을 사용한 예시

try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
  stream.forEach(…);
}

위 코드에서는 try-with-resources 블록을 사용하여 findAllByCustomQueryAndStream() 메서드에서 반환된 Stream을 처리하고 있습니다. 이 방식으로 Stream을 사용하면, try 블록이 끝난 후 자동으로 Stream이 닫힙니다.

주의사항

현재 모든 Spring Data 모듈이 Stream<T> 타입을 쿼리 메서드의 반환 타입으로 지원하지는 않습니다. 따라서 사용 중인 Spring Data 모듈이 이 기능을 지원하는지 확인이 필요합니다.

 

이 방식은 많은 데이터를 한번에 메모리에 로드하는 대신 스트리밍 방식으로 데이터를 처리할 수 있기 때문에 메모리 효율성을 높일 수 있다는 장점이 있습니다. 또한 Stream은 Java 8의 함수형 API와 결합되어 더 직관적인 데이터 처리 방식을 제공합니다.

Asynchronous Query Results

Asynchronous Query Results는 Spring의 비동기 메서드 실행 기능을 사용하여 쿼리를 비동기로 실행할 수 있는 방법을 설명합니다. 비동기 쿼리를 사용하면 메서드를 호출한 즉시 반환되며, 실제 쿼리는 Spring TaskExecutor에 제출된 작업으로 비동기적으로 수행됩니다.

비동기 쿼리와 Reactive 쿼리의 차이점

비동기 쿼리는 Reactive 쿼리와는 다릅니다. 두 가지는 서로 혼용해서는 안 됩니다. Reactive 쿼리는 Reactive Streams를 기반으로 한 완전히 다른 패러다임을 사용하므로, 별도의 문서에서 더 자세한 내용은 Reactive 지원에 대한 설명을 참조해야 합니다.

비동기 쿼리 예시

다음은 비동기 쿼리를 정의하는 방법에 대한 몇 가지 예시입니다:

@Async
Future<User> findByFirstname(String firstname);   // (1)

@Async
CompletableFuture<User> findOneByFirstname(String firstname);   // (2)
  1. Future 타입 사용: java.util.concurrent.Future 타입을 반환하여 비동기 메서드의 결과를 받을 수 있습니다. Future는 비동기 작업이 완료되면 해당 결과를 제공합니다. 하지만 결과를 받을 때까지 대기해야 하며, 예외적으로 블로킹이 발생할 수 있습니다.
  2. CompletableFuture 타입 사용: Java 8에서 제공하는 java.util.concurrent.CompletableFuture를 반환 타입으로 사용하면, Future와 달리 더 많은 기능을 제공합니다. 예를 들어, 비동기 작업이 완료되면 후속 작업(예: .thenApply(), .thenAccept())을 연쇄적으로 처리할 수 있습니다. CompletableFuture는 비동기 처리와 관련된 다양한 비블로킹 API를 지원하므로, 더 유연하고 효율적인 처리가 가능합니다.

주의사항

@Async로 비동기 메서드를 사용하려면 Spring에서 비동기 처리가 활성화되어 있어야 하며, 이를 위해서는 @EnableAsync 어노테이션을 사용하여 설정에서 비동기 지원을 활성화해야 합니다.

비동기 쿼리는 주로 작업의 성능을 높이거나, 특정 쿼리가 완료되기 전에 다른 작업을 동시에 수행할 필요가 있을 때 유용합니다. FutureCompletableFuture 모두 비동기 결과를 비동기적으로 처리할 수 있는 방법을 제공하지만, CompletableFuture는 더 많은 기능과 유연성을 제공한다는 차이점이 있습니다.