将 ENUM 值存储到数据库中

Store ENUM value into database

我想使用 ENUM 将值映射到数据库 table 行:

BusinessCustomersSearchParams:

@Getter
@Setter
public class BusinessCustomersSearchParams {

    private String title;

    private List<String> status;

    private LocalDateTime createdAt;

    private LocalDateTime updatedAt;
}

规格:

@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);
}

属性转换器:

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 );
  }

}

枚举:

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
@Table(name = "business_customers")
public class BusinessCustomers implements Serializable {
   
    ..........
    @Convert( converter = BusinessCustomersStatusAttributeConverter.class )
    private BusinessCustomersStatus status;
    ......
}

完整代码示例:https://github.com/rcbandit111/Search_specification_POC

我发送带有参数 list?size=5&page=0&status=active,suspended 的 http 查询,我得到的结果是大写字母“status”:“ACTIVE”。

我想使用 status=active 从 FE 搜索并获取状态,但仅将符号 A 存储到数据库行字段中。

如何将 ENUM 密钥 A 存储到数据库中?

请注意 BusinessCustomersStatusAttributeConverter 中的 convertToDatabaseColumn() 方法。

应该return value.name()而不是value.getStatus()

为了在数据库中存储实际的枚举值,可以做两件事。

一,按照@PetarBivolarski 的建议,修改AttributeConverter 中的方法convertToDatabaseColumn 和return value.name() 而不是value.getStatus()。但是请注意,您还需要更新 convertToEntityAttribute 以考虑到该更改:

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 );
  }

}

如果您考虑一下,更直接的解决方案是将 status 字段保持为 @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;

    //...
}

另外,根据您的其余代码。

关于您的第二个问题,应用程序正在 returning "status":"ACTIVE" 因为在 BusinessCustomersFullDTO you are defining the status field as String and this field receives the result of the mapping process 中由 @MapstructBusinessCustomersMapper 执行。

要解决该问题,正如我之前建议的那样,您可以修改 Mapper 以处理所需的自定义转换:

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();
    }
}

如果您不喜欢这个解决方案,也许您可​​以采用不同的方法:它将包含以下内容。这个想法是修改 BusinessCustomersFullDTO 的 Jackson 序列化和反序列化行为。事实上,在你的用例中只需要修改序列化逻辑。

首先,根据BusinessCustomersStatus定义BusinessCustomersFullDTO中的status字段:

@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;
}

要完成解决方案,请在 BusinessCustomersStatus 枚举中执行以下更改:

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)
                );
            }
        }
    }
}

请注意包含 @JsonValue@JsonCreator 注释:后者用于反序列化,这对我来说在您的应用程序中似乎是不必要的,但以防万一。

请查看提供的 Jackson 注释的 relevant documentation