I want to create a search Specification using ENUM values:
Search ENUM:
public enum BusinessCustomersStatus {
A("active"),
O("onboarding"),
N("not_verified"),
V("verified"),
S("suspended"),
I("inactive");
private String status;
BusinessCustomersStatus(String status)
{
this.status = status;
}
}
Search DTO:
@Getter
@Setter
public class BusinessCustomersSearchParams {
private String title;
private List<BusinessCustomersStatus> status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Search Specification:
public Page<BusinessCustomersFullDTO> findBusinessCustomers(BusinessCustomersSearchParams params, Pageable pageable)
{
Specification<BusinessCustomers> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (params.getTitle() != null) {
predicates.add(cb.like(cb.lower(root.get("title")), "%" params.getTitle().toLowerCase() "%"));
}
Optional<BusinessCustomersStatus> optStatus = EnumSet.allOf(BusinessCustomersStatus.class)
.stream()
.filter(e -> e.name().equals(params.getStatus()))
.findAny();
if(optStatus.isPresent()){
final List<BusinessCustomersStatus> statuses = params.getStatus();
if (statuses != null && !statuses.isEmpty()){
predicates.add(root.get("status").in(statuses));
}
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
return businessCustomersService.findAll(spec, pageable).map(businessCustomersMapper::toFullDTO);
}
Entity:
@Entity
@Table(name = "business_customers")
public class BusinessCustomers implements Serializable {
..........
@Enumerated(EnumType.STRING)
@Column(name = "status", length = 20)
private BusinessCustomersStatus status;
......
}
But I get this error:
java.lang.IllegalArgumentException: No enum constant org.service.businesscustomers.BusinessCustomersStatus.4d29e059cf] with root cause
java.lang.IllegalArgumentException: No enum constant org.service.businesscustomers.BusinessCustomersStatus.4d29e059cf
at java.base/java.lang.Enum.valueOf(Enum.java:273)
Do you know how I can fix this issue?
CodePudding user response:
I think you are mixing several of the options exposed in my answer to your original question.
Assuming that you are defining status
as a List
as in the example you provided:
@Getter
@Setter
public class BusinessCustomersSearchParams {
private String title;
private List<BusinessCustomersStatus> status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
You can try using in your Specification
either an in
clause:
public Page<BusinessCustomersFullDTO> findBusinessCustomers(BusinessCustomersSearchParams params, Pageable pageable) {
Specification<BusinessCustomers> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (params.getTitle() != null) {
predicates.add(cb.like(cb.lower(root.get("title")), "%" params.getTitle().toLowerCase() "%"));
}
final List<BusinessCustomersStatus> statuses = params.getStatus();
if (statuses != null && !statuses.isEmpty()){
predicates.add(root.get("status").in(statuses));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
return businessCustomersService.findAll(spec, pageable).map(businessCustomersMapper::toFullDTO);
}
Or you can iterate over the status
collection in order to verify each value and build an or
predicate with the required filter criteria:
public Page<BusinessCustomersFullDTO> findBusinessCustomers(BusinessCustomersSearchParams params, Pageable pageable) {
Specification<BusinessCustomers> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (params.getTitle() != null) {
predicates.add(cb.like(cb.lower(root.get("title")), "%" params.getTitle().toLowerCase() "%"));
}
final List<BusinessCustomersStatus> statuses = params.getStatus();
if (statuses != null && !statuses.isEmpty()){
List<Predicate> statusPredicates = new ArrayList<Predicate>();
EnumSet businessCustomersStatusEnumSet = EnumSet.allOf(BusinessCustomersStatus.class)
statusPredicates.forEach(status -> {
Optional<BusinessCustomersStatus> optStatus = businessCustomersStatusEnumSet
.stream()
.filter(e -> e.name().equals(status))
.findAny();
if(optStatus.isPresent()){
statusPredicates.add(cb.equal(root.get("status"), cb.literal(status)));
}
});
predicates.add(
cb.or(statusPredicates.toArray(new Predicate[statusPredicates.size()]))
);
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
return businessCustomersService.findAll(spec, pageable).map(businessCustomersMapper::toFullDTO);
}
The first of the provided approaches is the one that should be used in a typical use case, although the last can probably solve the problem by removing the incorrect values, but the actual question remains: why you are receiving the incorrect enum value in first place? Please, review your database records seeking for incorrect ones, and be sure - sorry for saying, but sometimes it happens, I faced myself - that you use everywhere the annotation value, A
, for instance, and not the value associated with it, active
, continuing with the example; they are the values that should be stored in the database, those to be submitted by your frontend, etc.
CodePudding user response:
You should define that enum
with constant values, then the values won't be instanceof
something (maybe it doesn't like the enumeration for some other reason, but it still reads no enum constant
).
public static final int STATUS_ACTIVE = 0;
public static final int STATUS_ONBOARDING = 1;
public static final int STATUS_NOT_VERIFIED = 2;
public static final int STATUS_VERIFIED = 3;
public static final int STATUS_SUSPENDED = 4;
public static final int STATUS_INACTIVE = 5;
public enum BusinessCustomersStatus {
STATUS_ACTIVE,
STATUS_ONBOARDING,
STATUS_NOT_VERIFIED,
STATUS_VERIFIED,
STATUS_SUSPENDED,
STATUS_INACTIVE
}
And @Enumerated
should then be EnumType.ORDINAL
:
@Enumerated(EnumType.ORDINAL)
@Column(name = "status")