Closed
Description
It's necessary to stream data for batch processing, Spring Data JPA supports return Stream
type, but it must be called in @Transactional
methods, and sometimes it failed even @Transactional
presented, for example:
@Transactional(readOnly = true)
public ResponseEntity<StreamingResponseBody> download(@SortDefault(sort = "id") Sort sort) {
return ResponseEntity.ok(outputStream -> {
try (Stream<User> all = userRepository.findBy(sort)) {
// write user to outputStream
}
});
}
I fixed it by adding a default method to repository:
Stream<User> streamAllBy(Sort sort);
@Transactional(readOnly = true)
default void forEach(Sort sort, Consumer<User> consumer) {
// avoid org.springframework.dao.InvalidDataAccessApiUsageException
// see JpaQueryExecution.StreamExecution::doExecute
try (Stream<T> all = streamAllBy(sort)) {
all.forEach(consumer);
}
}
public ResponseEntity<StreamingResponseBody> download(@SortDefault(sort = "id") Sort sort) {
return ResponseEntity.ok(outputStream -> {
userRepository.forEach(sort, user -> {
// write user to outputStream
});
});
}
I hope Spring Data JPA can provide a repository fragment such as:
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;
public interface StreamingRepository<T> {
Stream<T> streamAllBy(Sort sort);
@Transactional(readOnly = true)
default void forEach(Consumer<T> consumer) {
forEach(Sort.unsorted(), consumer);
}
@Transactional(readOnly = true)
default void forEach(Sort sort, Consumer<T> consumer) {
try (Stream<T> all = streamAllBy(sort)) {
all.forEach(consumer);
}
}
}
Reference document should be improved about streaming supports also whether this advice accepted or not.