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.