Funkcja map na liście 2-elementowej

0

Cześć,

Stosując funkcję java .map() z klasy Stream z biblioteki VAVR i wykorzystując JPA i Hibernatowe dirty-checking, updatujemy rekord, natomiast updatuje się tylko raz, bo tylko raz wchodzi do metody, mimo, że lista, po której streamujemy ma 2 elementy. Kod źródłowy poniżej:

@RequiredArgsConstructor
class AgreementService {
    private final AgreementFacade agreementFacade;
    private final UserFinderFacade userFinderFacade;
    private final UserFacade userFacade;
 
    AddressEntity create(AddressEntity addressEntity) {
        return Option.of(addressEntity)
                .map(this::createAgreement)
                .map(Agreement::getId)
                .map(agreement -> updateUsers(agreement, addressEntity)
                .map(agreementId -> deleteOldAgreement(addressEntity, agreementId))
                .getOrElseThrow(UnexpectedException::new);
    }
 
    private Agreement createAgreement(AddressEntity addressEntity) {
        return Option.of(addressEntity.getId())
                .map(Agreement::restore)
                .map(agreementFacade::save)
                .getOrElseThrow(UnexpectedException::new);
    }
 
    private long updateUsers(long newAgreementId, AddressEntity addressEntity) {
        return Option.of(addressEntity)
                .map(AddressEntity::getId)
                .flatMap(id -> agreementFacade.findByAddressIdAndAgreementStatus(id, AgreementStatus.REOPENED))
                .transform(
                        agreement -> agreement
                                .map(Agreement::getId)
                                .map(userFinderFacade::findUsersByAgreementId)
                                .getOrElse(List::empty)
                                .toStream()
                                .map(user -> userFacade.assignAgreement(user.getId(), newAgreementId))
                                .transform(__ -> agreement.get().getId())
                );
    }
 
    private AgreementEntity deleteOldAgreement(AddressEntity foundAddress, Long agreementId) {
        agreementFacade.deleteByAgreementId(agreementId);
        return foundAddress;
    }
}

Natomiast kod metody userFacade.assignAgreement() wygląda tak:ś

private UserDto assignAgreement(final long userId, final long agreementId) {
  return userRepository.findById(userId)
      .map(user -> user.agreementId(agreementId))
      .map(userMapper::map)
      .getOrElseThrow(UnexpectedException::new);
}

Problem jest w tym, że w linijce, gdzie szukamy agreement metodą userFinderFacade::findUsersByAgreementId), zwraca 2 elementy, dalej metoda .toStream() ma także 2 elementy, natomiast po wywołaniu tej metody, userFacade.assignAgreement() wywołuje się tylko raz. Po zamianie tych metod na te z java.util to wygląda tak samo. Po przerobieniu tego kodu tak, żeby zamiast metody .map() zastosować .forEach(), metoda ta wykonuje się 2 razy. Czy moglibyście pomóc? Stawiałem gdzieś na transakcje może, ale to raczej nie to. Na encjach mamy także @DynamicInsert i @DynamicUpdate, ale to chyba też nie powinno mieć wpływu? Z góry dzięki za pomoc

4
chivy napisał(a):

właśnie tutaj pojawia się problem, czym zastąpić taki "nieoczekiwany błąd"? Lepiej tutaj zwrócić eithera, zamiast rzucać wyjątek?

O tym to nie jedną książkę napisali ale z punktu widzenia Javy:

  • Jeśli interesuje Cię typ błędu bo z niektórymi coś można zrobić w metodzie wyżej to Either
  • Jeśli nie interesuje Cię co się wysypało, a sam fakt, że coś mogło pójść nie tak to Option
  • Jeśli w danej funkcji kilka rzeczy może się nie udać i chcesz mieć informację o każdej z nich to Validation
  • Jeśli dana funkcja jest krytyczna dla danego flow i nie ma szans na żaden sensowny recover to i rzucenie wyjątku jest ok. Ja mimo wszystko wolałbym mieć Either czy Option na poziomie typów co by mieć świadomość, że może się coś nie udać. Z drugiej strony nie oszukujmy się - Java nie jest i nie będzie językiem funkcyjnym i czasami jest lepiej podejść do problemów po "Javowemu" niż na siłę rzeźbić w przysłowiowym gównie
0

Problem rozwiązany, chodziło o to, że operacje na streamie wykonywały się tylko raz, bo nie było operacji typu terminate. Po jej dodaniu, wszystko śmiga tak jak powinno.

1 użytkowników online, w tym zalogowanych: 0, gości: 1