Why does JPA Repository save method does not update entity details on production?

Viewed 932

I am using spring data JPA I am trying to update the entity using the JPARepository method save() to update the entity details. Code I have written to update the entity works fine in development as expected. But the same code does not work on the production server it does not give any error only the code written inside the map() does not work. Below is my code

public Long updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
        return qrCodeRepo.findByBusinessId(businessId).businessQrCode.stream().map(qr -> {
            qr.setMenuUrl(menuUrl);
            qr.setMenuType(menuType);
            return qrCodeRepo.save(qr);
        }).count();

    }
3 Answers

The problem is that count

may choose to not execute the stream pipeline (either sequentially or in parallel) if it is capable of computing the count directly from the stream source. In such cases no source elements will be traversed and no intermediate operations will be evaluated. Behavioral parameters with side-effects, which are strongly discouraged except for harmless cases such as debugging, may be affected

And exactly that happens in your case. The solution is to neither use map or count at all since you do not need / use them for what they are supposed to be used for and instead use a single forEach instead:

public void updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
    List<BusinessQRCode> businessQrCode = qrCodeRepo.findByBusinessId(businessId);
    businessQrCode.stream().forEach(qr -> {
        qr.setMenuUrl(menuUrl);
        qr.setMenuType(menuType);
        qrCodeRepo.save(qr);
    });
}

Note that you could just do the exact same thing with a for loop with less calls, same amount of lines and without unnecessarily using streams:

public void updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
    List<BusinessQRCode> businessQrCode = qrCodeRepo.findByBusinessId(businessId);
    for (BusinessQRCode qr : businessQrCode) {
        qr.setMenuUrl(menuUrl);
        qr.setMenuType(menuType);
        qrCodeRepo.save(qr);
    };
}

just my two cents.

You should always return Optional from query results. You may have it as:

In repository:

Optional<List<BusinessQRCode>> findByBusinessId(Long businessId);

In Service:

public void updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {

// Fetch iterate, if exist
qrCodeRepo.findByBusinessId(businessId).ifPresent(qrCodes_ -> {

  qrCodes_.forEach(code_-> {
     qr.setMenuUrl(menuUrl);
     qr.setMenuType(menuType);
  });

  qrCodeRepo.saveAll(qrCodes_);

});

Why use map when you are returning same qr. You can skip that part and also the collect function. We already have the list of qrCodes, just iterate them and set the values then save all at once.

After making some changes in code it work as expected on development and production. I just change the object value in the map() and return after that collect the object to list using the Collectors.toList() method and again use forEach() loop and update the entity data. Now code works as expected.

public void updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
        List<BusinessQRCode> businessQrCode = qrCodeRepo.findByBusinessId(businessId);
        businessQrCode.stream().map(qr -> {
            qr.setMenuUrl(menuUrl);
            qr.setMenuType(menuType);
            return qr;
        }).collect(Collectors.toList()).forEach(qr -> qrCodeRepo.save(qr));

    }
Related