I wan to use ENUM to map values into database table rows:
BusinessCustomersSearchParams:
@Getter
@Setter
public class BusinessCustomersSearchParams {
private String title;
private List<String> status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Specification:
@Override
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("description")), "%" + params.getTitle().toLowerCase() + "%"));
}
final List<String> statuses = Optional.ofNullable(params.getStatus()).orElse(Collections.emptyList());
if (statuses != null && !statuses.isEmpty()){
List<BusinessCustomersStatus> statusesAsEnum = statuses.stream()
.map(status -> BusinessCustomersStatus.fromStatus(status))
.collect(Collectors.toList())
;
predicates.add(root.get("status").in(statusesAsEnum));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
return businessCustomersService.findAll(spec, pageable).map(businessCustomersMapper::toFullDTO);
}
AttributeConverter:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter
public class BusinessCustomersStatusAttributeConverter
implements AttributeConverter<BusinessCustomersStatus, String> {
public String convertToDatabaseColumn( BusinessCustomersStatus value ) {
if ( value == null ) {
return null;
}
return value.getStatus();
}
public BusinessCustomersStatus convertToEntityAttribute( String value ) {
if ( value == null ) {
return null;
}
return BusinessCustomersStatus.fromStatus( value );
}
}
Enum:
package org.merchant.database.service.businesscustomers;
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;
}
public String getStatus() {
return status;
}
public static BusinessCustomersStatus fromStatus(String status) {
switch (status) {
case "active": {
return A;
}
case "onboarding": {
return O;
}
case "not_verified": {
return NV;
}
case "verified": {
return V;
}
case "suspended": {
return S;
}
case "inactive": {
return I;
}
default: {
throw new UnsupportedOperationException(
String.format("Unkhown status: '%s'", status)
);
}
}
}
}
Entity:
@Entity
@Table(name = "business_customers")
public class BusinessCustomers implements Serializable {
..........
@Convert( converter = BusinessCustomersStatusAttributeConverter.class )
private BusinessCustomersStatus status;
......
}
Full code example: https://github.com/rcbandit111/Search_specification_POC
I send http query with params list?size=5&page=0&status=active,suspended
and I get result with capital letters "status": "ACTIVE".
I wan to search and get status from FE for status using status=active
but store into database row field only symbol A.
How I can store into database the ENUM key A?
In order to store the actual enumeration value in the database, you can do two things.
One, as suggested by @PetarBivolarski, modify the method convertToDatabaseColumn
in AttributeConverter
and return value.name()
instead of value.getStatus()
. But please, be aware that in addition you will need to update the convertToEntityAttribute
as well to take into account that change:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter
public class BusinessCustomersStatusAttributeConverter
implements AttributeConverter<BusinessCustomersStatus, String> {
public String convertToDatabaseColumn( BusinessCustomersStatus value ) {
if ( value == null ) {
return null;
}
return value.name();
}
public BusinessCustomersStatus convertToEntityAttribute( String value ) {
if ( value == null ) {
return null;
}
return BusinessCustomersStatus.valueOf( value );
}
}
If you think about it, a more straightforward solution will be just keep the status
field as @Enumerated
:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Entity
@Table(name = "business_customers")
public class BusinessCustomers {
//...
@Enumerated(EnumType.STRING)
@Column(name = "status", length = 20)
private BusinessCustomersStatus status;
//...
}
It is in addition more according to the rest of your code.
Regarding your second problem, the application is returning "status":"ACTIVE"
because in BusinessCustomersFullDTO
you are defining the status field as String
and this field receives the result of the mapping process performed by @Mapstruct
and BusinessCustomersMapper
.
To solve that issue, as I suggested you previously, you can modify your Mapper
to handle the desired custom conversion:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.merchant.config.BaseMapperConfig;
import org.merchant.database.entity.BusinessCustomers;
import org.merchant.database.service.businesscustomers.BusinessCustomersStatus;
import org.merchant.dto.businesscustomers.BusinessCustomersFullDTO;
@Mapper(config = BaseMapperConfig.class)
public interface BusinessCustomersMapper {
@Mapping(source = "status", target = "status", qualifiedByName = "businessCustomersToDTOStatus")
BusinessCustomersFullDTO toFullDTO(BusinessCustomers businessCustomers);
@Named("busineessCustomersToDTOStatus")
public static String businessCustomersToDTOStatus(final BusinessCustomersStatus status) {
if (status == null) {
return null;
}
return status.getStatus();
}
}
If you do not prefer this solution, perhaps you can take a different approach: it will consist in the following. The idea is modifying the Jackson serialization and deserialization behavior of BusinessCustomersFullDTO
. In fact, in your use case only is necessary to modify the serialization logic.
First, define the status
field in BusinessCustomersFullDTO
in terms of BusinessCustomersStatus
as well:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class BusinessCustomersFullDTO {
private long id;
private String name;
private String businessType;
private BusinessCustomersStatus status;
private String description;
private String country;
private String address1;
}
To complete the solution, please, perform the following changes in the BusinessCustomersStatus
enum:
public enum BusinessCustomersStatus {
A("active"),
O("onboarding"),
NV("not_verified"),
V("verified"),
S("suspended"),
I("inactive");
private String status;
BusinessCustomersStatus(String status)
{
this.status = status;
}
// Define the status field as the enum representation by using @JsonValue
@JsonValue
public String getStatus() {
return status;
}
// Use the fromStatus method as @JsonCreator
@JsonCreator
public static BusinessCustomersStatus fromStatus(String status) {
if (StringUtils.isEmpty(status)) {
return null;
}
switch (status) {
case "active": {
return A;
}
case "onboarding": {
return O;
}
case "not_verified": {
return NV;
}
case "verified": {
return V;
}
case "suspended": {
return S;
}
case "inactive": {
return I;
}
default: {
throw new UnsupportedOperationException(
String.format("Unkhown status: '%s'", status)
);
}
}
}
}
Note the inclusion of the @JsonValue
and @JsonCreator
annotations: the later is using for deserialization which seems unnecessary to me in your application, but just in case.
Please, see the relevant documentation of the provided Jackson annotations.
Notice your convertToDatabaseColumn()
method in BusinessCustomersStatusAttributeConverter
.
It should return value.name()
instead of value.getStatus()
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.