来自JPA存储库的Java 8流

时间:2014-09-17 17:01:15

标签: parallel-processing java-8 spring-data-jpa java-stream spliterator

我想从JPA Repository创建一个Stream。目标是将来自Repo(可能超过一百万)的实体映射到其他实体,而这些实体又将存储在另一个Repo中。

到目前为止,我构建了一个收集器,它将收集给定数量(例如1000个)的实体,然后将它们存储到目标回购中。这将在并行流中工作。我现在需要的是从源Repo获取实体的好方法,并在需要时将它们提供给Stream。

到目前为止,最有希望的是实现供应商(http://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html)以通过生成来构建流,但是当查询源Repo不提供另一个实体时,我没有找到终止流程的方法。 / p>

任何指针?

4 个答案:

答案 0 :(得分:2)

我们最近在最新的Spring Data Fowler Release Train的RC1版本中添加了对Spring Data JPA(和MongoDB)的支持。

Example for Stream returned by a delegating default method Example for "real streaming" Stream

答案 1 :(得分:1)

如果您能够将来源表达为Supplier,那么您也可以实施Spliterator。如果有新项目或返回Supplier.get,您将实施boolean tryAdvance(Consumer)而不是accept而不会返回新值,而是在Consumer上调用false。除此以外。对于大多数情况,与Iterator相比,这简化了实现,您必须处理可以按任意顺序调用的两个方法hasNextnext

您必须为Spliterator实现更多方法,但幸运的是,有直接的方法可以实现它们。

public Spliterator<T> trySplit() {
    return null;// simple answer when splitting is not supported
}
public long estimateSize() {
    return Long.MAX_VALUE; // the value which should be used for UNKNOWN
}
public int characteristics() {
    return 0; // no flags but check out whether some flags fit
}

对于characteristics方法,值得查找the possible values,因为如果它们符合您的源特征,它们可能会改进流处理。

获得Spliterator后,您可以创建一个流:

Stream<T> s=StreamSupport.stream(sp, false);

如果您的来源更符合hasNext / next模式,您可以实现普通Iterator并让JRE创建Spliterator,如{{3}中所述}}

答案 2 :(得分:0)

一个简单的例子可能是:

  @Repository
  public class MyEntityRepository extends CrudRepository<MyEntity, Long> {           
  }

  @Component
  public class MyEntityService {

       @Autowired
       private MyEntityRepository myEntityRepository;


       public void() {
            // if the findAll() method returns List
            Stream<MyEntity> streamFromList = myEntityRepository.findAll().stream();


            // if the findAll() method returns Iterable
            Stream<MyEntity> streamFromIterable = StreamSupport.stream(myEntityRepository.findAll().spliterator(), true);

       }
  } 

答案 3 :(得分:0)

好的,感谢所有的贡献。我将所说的内容与我所需要的内容相结合。也许实施将澄清我想要的开始。

我创建了两个类, RepositryCollector RepositorySpliterator

public class RepositoryCollector<T> implements Collector<T, Tuple2<Integer,List<T>>, Integer>{

    private JpaRepository<T, ?> repository;
    private int threshold;

    public BinaryOperator<Tuple2<Integer, List<T>>> combiner() {
        return (listTuple, itemsTuple) -> {
            List<T> list = listTuple._2;
            List<T> items = itemsTuple._2;
            list.addAll(items);
            int sum = listTuple._1 + itemsTuple._1;
            if(list.size() >= this.threshold){
                this.repository.save(list);
                this.repository.flush();
                list = new LinkedList<>();
            }
            return new Tuple2<>(sum, list);
        };
    }
}

我省略了收集器所需的其他功能,因为组合器中存在所有相关信息。同样适用于Spliterator。

public class RepositorySpliterator<T> implements Spliterator<T> {

    private Slice<T> slice;
    private Function<Pageable, Slice<T>> getSlice;
    private Iterator<T> sliceIterator;

    public RepositorySpliterator(Pageable pageable, Function<Pageable, Slice<T>> getSlice) {
        this.getSlice = getSlice;
        this.slice = this.getSlice.apply(pageable);
        this.sliceIterator = slice.iterator();
    }

    @Override
    public boolean tryAdvance(Consumer<? super T> action) {
        if(sliceIterator.hasNext()) {
            action.accept(sliceIterator.next());
            return true;
        } else if (slice.hasNext()) {
            this.slice = getSlice.apply(slice.nextPageable());
            this.sliceIterator = this.slice.iterator();
            if(sliceIterator.hasNext()){
                action.accept(sliceIterator.next());
                return true;
            }
        }
        return false;
    }

    public Stream<T> getStream(boolean parallel){
        return StreamSupport.stream(this, parallel);
    }
}

正如你所看到的,我输入了一个帮助函数来生成我需要的Stream。也许这有点草率但是......嗯。

所以现在我只需要在我的映射类中使用几行代码来实现目标。

    public void start(Timestamp startTimestamp, Timestamp endTimestamp) {
        new RepositorySpliterator<>(
                new PageRequest(0, 10000), pageable -> sourceRepository.findAllBetween(startTimestamp, endTimestamp, pageable))
                .getStream(true)
                .map(entity -> mapToTarget(endTimestamp, entity))
                .collect(new RepositoryCollector<>(targetRepository, 1000));
    }

映射器将从源中获取10000个实体,将它们倒入流池中,以便映射和存储它们。每当一个流用完新实体时,将获取一个新的批次并将其送入同一个流池。

如果我的实施中存在明显的错误,请随时评论和改进!