I am using Sring boot and I have an entity Product that has a Map<String,String> that represent the attributes of the product. I want do a controller that permit the user to send a List of attributes with the comparision operator of each attribute, and then find the Products that meet the conditions.
I tried doing the following:
This is my Entity
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ElementCollection
@MapKeyColumn(name="name")
@Column(name="value")
private Map<String,String> attributes;
}
Then I use a searchCriteria to define of what attribute I am searching
public class SearchCriteria {
private String key;
private String operation;
private String value;
}
Finally my Specification that for now only find by a single attribute, but this is the part that does not work because of the following error: java.lang.IllegalStateException: Basic paths cannot be dereferenced
public class ProductSpecification implements Specification<Product>{
private SearchCriteria searchCriteria;
public ProductSpecification(SearchCriteria searchCriteria) {
this.searchCriteria = searchCriteria;
}
@Override
public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
String operation = searchCriteria.getOperation();
switch (operation) {
case "==":
return criteriaBuilder.equal(root.join("attributes").get(searchCriteria.getKey()),searchCriteria.getValue());
case "<":
return criteriaBuilder.lessThan(root.join("attributes").get(searchCriteria.getKey()),searchCriteria.getValue());
case ">":
return criteriaBuilder.greaterThan(root.join("attributes").get(searchCriteria.getKey()),searchCriteria.getValue());
default:
return null;
}
}
}
@Service
public class ProductService {
@Autowired
ProductRepository productRepository;
public List<Product> findBy(SearchCriteria searchCriteria){
ProductSpecification productSpecification = new ProductSpecification(searchCriteria);
return productRepository.findAll(productSpecification);
}
@Controller
@RequestMapping("/api/product")
public class ProductController {
@Autowired
ProductService productService;
@GetMapping("findBy")
public ResponseEntity<?> findBy(@RequestBody SearchCriteria searchCriteria){
List<Product> products = productService.findBy(searchCriteria);
return new ResponseEntity<>(products, HttpStatus.OK);
}
I finally solved it in the following way.