Can you use Springs JdbcTemplate to stream data [duplicate]
I found this very helpful article on how to do it: Using the Java 8 Stream API with Spring’s JdbcTemplate.
Inspired by this article I have made an improved implementation:
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet; import org.springframework.stereotype.Component; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Iterator; import java.util.Spliterator; import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; @Slf4j @Component @RequiredArgsConstructor public class QueryStreamer < private final NamedParameterJdbcTemplate jdbcTemplate; /** * Execute query and make result available for a Stream> consumer * @param sql * @param parameters * @param clazz * @param consumer * @param */ public void queryForStream( String sql, MapSqlParameterSource parameters, Class clazz, java.util.function.Consumer consumer ) < queryForStream(sql, parameters, resultSetStream -> < BeanPropertyRowMappermapper = new TrimmingBeanPropertyRowMapper<>(clazz); consumer.accept(resultSetStream.map(r -> mapIt(r, mapper))); return null; >); > // Build a Stream private void queryForStream( String sql, MapSqlParameterSource parameters, java.util.function.UnaryOperator operator ) < jdbcTemplate.query(sql, parameters, resultSet -> < final ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet); final boolean parallel = false; Spliteratorspliterator = Spliterators.spliteratorUnknownSize(new Iterator<>() < @Override public boolean hasNext() < return rowSet.next(); >@Override public ResultSet next() < return resultSet; >>, Spliterator.IMMUTABLE); return operator.apply(StreamSupport.stream(spliterator, parallel)); >); > private static T mapIt(ResultSet resultSet, BeanPropertyRowMapper mapper) < try < return mapper.mapRow(resultSet, 0); >catch (SQLException e) < throw new RuntimeException(e); >> >
And this is how you could use it in a DAO — implemented in Kotlin because of the nice multiline string literal support:
import MyEntity import QueryStreamer import org.springframework.jdbc.core.namedparam.MapSqlParameterSource import org.springframework.stereotype.Component import java.util.function.Consumer import java.util.stream.Stream @Component open class MyEntityDAO(private val queryStreamer: QueryStreamer) < val sql = """ SELECT column_1, column_2, column_3 FROM my_entity_table WHERE some_criteria = 'met' """.trimIndent() fun streamIt(consumer: Consumer>) < queryStreamer.queryForStream(sql, MapSqlParameterSource(), MyEntity::class.java, consumer) >>
Поддержка Spring Data Java 8
Spring Data теперь поддерживает основные функции Java 8, такие какOptional,Stream API иCompletableFuture.
В этой быстрой статье мы рассмотрим несколько примеров того, как мы можем использовать их с фреймворком.
2. Optionalс
Начнем с методов репозитория CRUD, которые теперь оборачиваются вOptional:
public interface CrudRepository extends Repository < OptionalfindById(ID id); >
При возврате экземпляраOptional это полезный намек на то, что существует вероятность того, что значение может не существовать. Более подробную информацию о Optional можно найти вhere.
Все, что нам теперь нужно сделать, это указать тип возвращаемого значения какOptional:
public interface UserRepository extends JpaRepository < OptionalfindOneByName(String name); >
3. Stream API
Spring Data также обеспечивает поддержку одной из наиболее важных функций Java 8 — APIStream.
Раньше, когда нам нужно было возвращать более одного результата, нам нужно было возвращать коллекцию:
public interface UserRepository extends JpaRepository < // . ListfindAll(); // . >
Одной из проблем этой реализации было потребление памяти.
Нам пришлось с нетерпением загружать и хранить все найденные объекты в нем.
Мы могли бы улучшить, используя пейджинг:
public interface UserRepository extends JpaRepository < // . PagefindAll(Pageable pageable); // . >
В некоторых сценариях этого достаточно, но в других случаях разбиение на страницы — не лучший вариант из-за большого количества запросов, необходимых для получения данных.
Благодаря Java 8Stream API и провайдерам JPA — теперь мы можемdefine that our repository method returns just a Stream of objects:
public interface UserRepository extends JpaRepository < // . StreamfindAllByName(String name); // . >
Spring Data использует реализацию, зависящую от поставщика, для потоковой передачи результата (Hibernate используетScrollableResultSet, EclipseLink используетScrollableCursor). Это уменьшает количество потребления памяти и запросов к базе данных. Из-за этого он намного быстрее, чем два упомянутых ранее решения.
Processing data with a Stream requires us to close a Stream when we finish it.
Это можно сделать, вызвав методclose() дляStream или используяtry-with-resources:
try (Stream foundUsersStream = userRepository.findAllByName(USER_NAME_ADAM)) < assertThat(foundUsersStream.count(), equalTo(3l));
We must also remember to call a repository method within a transaction. В противном случае мы получим исключение:
org.springframework.dao.InvalidDataAccessApiUsageException: вы пытаетесь выполнить метод потокового запроса без внешней транзакции, которая поддерживает соединение, так чтоStream может быть фактически использован. Убедитесь, что код, использующий поток, использует@Transactional или любой другой способ объявления транзакции (только для чтения).
4. CompletableFutureс
Spring Data repositories can run asynchronously with the support of Java 8’s CompletableFuture и механизм Spring для выполнения асинхронных методов:
@Async CompletableFuture findOneByStatus(Integer status);
Клиент, который вызывает этот метод, немедленно вернет будущее, но метод продолжит выполнение в другом потоке.
Более подробную информацию об обработкеCompletableFuture можно найти вhere.
5. Заключение
В этом руководстве мы показали, как функции Java 8 работают вместе с Spring Data.
Доступна полная реализация примеровover on Github.