'How to remove an element from a Set inside a Page of items based on a Condition using Java 8

I have a Page of Farmers coming from a JPA repository call findAll method. Each Farmer consists of a Set of Farms which are also coming in the repository call like so:

public class Farmer {

private String name;
@ManyToMany
@JoinTable(name = "farmer_asset", joinColumns = @JoinColumn(name = "farmer_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "asset_id", referencedColumnName = "id"))
private Set<Asset> assets;
}

Asset has a property called isActive which indicates that the asset is active or not;

In my Page of Farmers I want to select only farmers' Assets which are active;

I tried with @WhereJoinTable(clause = "is_active ='true'")

However, I learnt that the is_active property is supposed to be in the intermediate ManyToMany relation Table farmer_asset

Now I am not in a position to change the entity structure, and use @WhereJoinTable(clause = "is_active ='true'").

So, I thought of trying to filter the values after making the JPA call.

However, iterating through each Farmer, then removing the inActive Assets and then adding it back to the Page and maintaining Page elements seemed too performance heavy operation.

I tried with normal Java Code which looks something like this:


Page<Farmer> farmers = farmerRepository.findAll(pageable);

List<Farmer> farmersList = new LinkedList<>();

for (FarmerDto eachFarmer : farmers.getContent()) {

   Set<Asset> eachFarmerAssets = 
   eachFarmer.getAssets();

   eachFarmerAssets.removeIf(e -> 
   !e.getIsActive());

   eachFarmer.setAssets(eachFarmerAssets);              
   
   farmersList.add(eachFarmer);
               }
         return new PageImpl<FarmerDto>(farmersList, pageable, farmers.getTotalElements());

I would like to do this is Java 8 using streams but I am not able to iterate a list and change its element which is in turn a Set<Asset>

Came here looking for some suggestions.

Thanks in advance.



Solution 1:[1]

You actually don't to reassign the same reference to Set<Asset> because even the set has been modified, it's still the same object which is "known" to a particular farmer.

And definitely you don't need streams for this task. Documentation suggests avoiding stateful operations and side-effects while designing stream pipe-lines.

Instead, you can use forEach() method on a list:

Page<Farmer> farmers = farmerRepository.findAll(pageable);
List<Farmer> farmersList = new farmers.getContent();
    
farmersList.forEach(farmer -> farmer.getAssets()
    .removeIf(e -> !e.getIsActive()));

Solution 2:[2]

You have to follow these steps:

  1. retrieve the list of farmer entities
  2. transform the list of entities to the list of farmer dtos
  3. for each farmer dto
    1. fill the data
    2. put the list of assert that are active
List<Farmer> farmers = farmerRepository.findAll(pageable).getContent(); //1
List<FarmerDto> farmersList = farmers.stream() // 2
        .map(farmer -> { // 3
            FarmerDto farmerDto = new FarmerDto();
            // fill farmerDto 3.1
            farmerDto.setAssets(farmer.getAssets().stream().filter(Farmer::getIsActive()).toList()); //3.2
            return farmerDto;
        }).toList();
return new PageImpl<FarmerDto>(farmersList, pageable, farmers.getTotalElements());                         

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 frascu