java—可以使用SpringJDBCTemplate来流式传输数据吗

n3schb8v  于 2021-06-27  发布在  Java
关注(0)|答案(1)|浏览(402)

springs jdbctemplate可以用于查询数据库并使结果作为流提供吗?

jrcvhitl

jrcvhitl1#

是的,但不是现成的。
我发现这篇关于如何做到这一点的非常有用的文章:将Java8流api与spring的jdbctemplate结合使用。
受本文启发,我改进了实现:

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{@literal <T>} consumer
   * @param sql
   * @param parameters
   * @param clazz
   * @param consumer
   * @param <T>
   */
  public <T> void queryForStream(
    String sql,
    MapSqlParameterSource parameters,
    Class<T> clazz,
    java.util.function.Consumer<Stream<T>> consumer
  ) {
    queryForStream(sql, parameters, resultSetStream -> {
      BeanPropertyRowMapper<T> mapper = new TrimmingBeanPropertyRowMapper<>(clazz);
      consumer.accept(resultSetStream.map(r -> mapIt(r, mapper)));
      return null;
    });
  }

  // Build a Stream<ResultSet>
  private void queryForStream(
    String sql,
    MapSqlParameterSource parameters,
    java.util.function.UnaryOperator<Stream<ResultSet>> operator
  ) {
    jdbcTemplate.query(sql, parameters, resultSet -> {
      final ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
      final boolean parallel = false;

      Spliterator<ResultSet> spliterator = 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> T mapIt(ResultSet resultSet, BeanPropertyRowMapper<T> mapper) {
    try {
      return mapper.mapRow(resultSet, 0);
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
  }

}

这就是如何在用kotlin实现的dao中使用它的方法,因为它支持多行字符串:

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<Stream<MyEntity>>) {
    queryStreamer.queryForStream(sql, MapSqlParameterSource(), MyEntity::class.java, consumer)
  }
}

就这样-玩得开心:-)

相关问题