I have a database with 3 tables:
Table A which contains data of A objects
Table B which contains data of B objects
Table C which contains data of C objects
A objects can have 0 or 1 B objects
B objects can have 0 or 1 C objects
(I know, these could be in just one table, but its just for the example)
I want to make a csv file from the whole database: each line should contain exactly one A object, optionally its B object, and optionally its C object.
For each table there is an asynchronous repository, that gives back a CompletionStage. So when I fetch A objects from the repositoryA, I get back a CompletionStage<List<A>>
. When it completes, I make a Map for every A object, fill it with the data of A and I call the repositoryB.getB(A.id)
, which gives back a CompletionStage<Optional<B>>
. If the B value is not present, I append a new line to my CSV file with the data inside of the map. If the B is present, I add its values to the map, and call repositoryC.getC(B.id)
which returns a CompletionStage<Optional<C>>
. If the C is present, I add its values to the Map, and add a new line to the CSV file, if it is not, then I just add the new line.
The creation of the CSV is done, when all CompletionStages are completed. I tried to use the CompletableFuture.allOf(), but since at the beginning i don't know how many CompletionStages there will be, I can't add all of them to the allOf method, so I think i would need to add the Completionstages dynamically somehow. Is it possible?
Currently I have a working solution, but it blocks after every B and C fetch, so I want to make the whole code nonblocking.
This is my nonblocking attempt, but its not working well, as some of the B and C futures aren't added to the list of futures, so the code does not wait for their completion:
CompletableFuture<List<CompletableFuture>> genereteCSV = repositoryA.getAs().thenApplyAsync(listA-> {
List<CompletableFuture> futures = new ArrayList<>();
for (A a : listA) {
Map<String, String> values = new Map<>();
addAvaluesToMap(values, A);
CompletableFuture Bfuture = repositoryB.getB(A.id).thenAcceptAsync((optionalB -> {
if (optionalB.isPresent()) {
addValuesToMap(values, B);
CompletableFuture Cfuture = repositoryC.getC(B.id).thenAcceptAsync(optionalC-> {
if (optionalC.isPresent()) {
addAvaluesToMap(values, C);
}
addMapValuesToCSV(values);
});
futures.add(Cfuture);
} else {
addMapValuesToCSV(values);
}
}));
futures.add(Bfuture);
}
return futures;
});
geerateCSV.thenApplyAsync(futureList-> CompletableFuture.allOf(futureList.toArray(new CompletableFuture<?>[0])))
.thenAccept(dummy->{System.out.println("CsV generation done");});