如何通过Spring Data JPA Specification按指定companyId过滤作物数据?
It looks like your current implementation isn't adding the companyId as a mandatory filter condition to your final query predicate, which is why you're getting results from all companies instead of just the specified one. Let's fix this with a few targeted changes:
Step 1: Update CropMasterSpecification to Enforce CompanyId
Your CropMasterSpecification already accepts a companyId in its constructor, but it isn't using it to build a filter. Let's modify the toPredicate method to always include the companyId match, and combine it with user-provided search criteria:
public class CropMasterSpecification implements Specification<CropMaster> { private SearchCriteria criteria; private Integer companyId; public CropMasterSpecification(final SearchCriteria criteria, final Integer companyId) { super(); this.criteria = criteria; this.companyId = companyId; } public SearchCriteria getCriteria() { return criteria; } @Override public Predicate toPredicate(Root<CropMaster> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { // First, create the mandatory companyId filter Predicate companyFilter = criteriaBuilder.equal(root.get("companyId"), companyId); // Handle user search criteria if present Predicate searchFilter = null; if (criteria != null) { if (criteria.getOperation().equalsIgnoreCase(">")) { searchFilter = criteriaBuilder.greaterThanOrEqualTo( root.<String>get(criteria.getKey()), criteria.getValue().toString()); } else if (criteria.getOperation().equalsIgnoreCase("<")) { searchFilter = criteriaBuilder.lessThanOrEqualTo( root.<String>get(criteria.getKey()), criteria.getValue().toString()); } else if (criteria.getOperation().equalsIgnoreCase(":")) { if (root.get(criteria.getKey()).getJavaType() == String.class) { searchFilter = criteriaBuilder.like( root.<String>get(criteria.getKey()), "%" + criteria.getValue() + "%"); } else { searchFilter = criteriaBuilder.equal(root.get(criteria.getKey()), criteria.getValue()); } } } // Combine the filters: companyId must match, plus any user search criteria return searchFilter != null ? criteriaBuilder.and(companyFilter, searchFilter) : companyFilter; } }
Note: I removed the unnecessary companyId operation branch from your original code — since companyId is a fixed filter from the endpoint parameter, users shouldn't pass it via the search query string.
Step 2: Modify CropMasterSpecificationsBuilder to Pass CompanyId
Update the builder's build method to accept the companyId parameter, so it can pass this value to each CropMasterSpecification it creates:
public class CropMasterSpecificationsBuilder { private final List<SearchCriteria> params; public CropMasterSpecificationsBuilder() { params = new ArrayList<>(); } public CropMasterSpecificationsBuilder with(String key, String operation, Object value) { params.add(new SearchCriteria(key, operation, value)); return this; } // Updated build method to accept companyId public Specification<CropMaster> build(Integer companyId) { // If no search params, return only the companyId filter if (params.isEmpty()) { return (root, query, cb) -> cb.equal(root.get("companyId"), companyId); } List<Specification<CropMaster>> specs = new ArrayList<>(); for (SearchCriteria param : params) { // Pass companyId to each specification specs.add(new CropMasterSpecification(param, companyId)); } // Combine all search specifications with AND logic Specification<CropMaster> combinedSpec = specs.get(0); for (int i = 1; i < specs.size(); i++) { combinedSpec = Specifications.where(combinedSpec).and(specs.get(i)); } return combinedSpec; } public final CropMasterSpecificationsBuilder with(SearchCriteria criteria) { params.add(criteria); return this; } }
Step 3: Update Your REST Service to Use the Updated Builder
In your getCropMasters method, pass the companyId parameter to the builder's build method, and mark companyId as required to avoid accidental null values:
public Page<CropMasterDTO> getCropMasters( @NotNull Integer companyId, Pageable pageable, LocalDateTime lastModifiedDateTime, Integer langaugeIdForUser, String search) { CropMasterSpecificationsBuilder builder = new CropMasterSpecificationsBuilder(); // Parse search parameters if provided if (search != null && !search.isBlank()) { Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); Matcher matcher = pattern.matcher(search + ","); while (matcher.find()) { builder.with(matcher.group(1), matcher.group(2), matcher.group(3)); } } // Build specification with companyId filter Specification<CropMaster> spec = builder.build(companyId); Page<CropMaster> cropMasters = cropMasterRepository.findAll(spec, pageable); // Map to DTOs and return CropMasterDTOMapper mapper = Mappers.getMapper(CropMasterDTOMapper.class); List<CropMasterDTO> cropMasterDTOs = mapper.cropMasterToDTOs(cropMasters.getContent()); return new PageImpl<>(cropMasterDTOs, pageable, cropMasters.getTotalElements()); }
Test the Fix
Now when you call your endpoint like this:
http://localhost:8080/cropMaster/list?companyId=YOUR_COMPANY_ID&search=cropName:Cucumber,scientificName:Cucumber
You'll only get results where companyId matches the value you passed, plus the search criteria from the search parameter.
内容的提问来源于stack exchange,提问作者vishalakshi

