Optimize your code by reducing disk access
When writing an algorithm that requires informations from a database you should always prefer collecting the required data first in order to feed your code.
Instead of:
newCities.forEach(city -> {City cityById = cityDao.findById(city.getId());...});
prefer:
List<City> cities = cityDao.find(filter);newCities.forEach(city -> {City cityById = cities.stream().filter(c -> c.getId().equals(city.getId())).findFirst().orElseThrow(() -> new RuntimeException("City not found"));...});
It does not mean you should always load data from your database and then filter it in Java ! Collect only the required data needed by your algorithm when making the database query.