Spring REST 与枚举一起使用

Spring REST 与枚举一起使用

概述

枚举是 java 语言的一个强大功能。它们是在 Java 5 中引入的。它们是一种特殊类型,允许我们声明一组预定义常量。它们提高了可读性,提供编译时检查并且类型安全。

在本文中,我们将扩展之前文章中客户端端点的功能。我们要做的就是向 类添加一个新的状态字段。然后,我们就可以根据状态进行持久化和搜索。

本文分为两个主要部分:

使用 JPA 映射枚举。 中的请求参数和枚举。

事不宜迟,让我们进入下一部分并编写一些代码。

使用 JPA 映射枚举

将枚举与数据库列映射的最简单方法是使用 . 包中的 @ 注释。该注释接受两种类型的映射。默认值为java枚举有什么用,这意味着枚举将存储为整数。另一种类型 将枚举存储为枚举值/常量。

让我们看看这是行动。步骤是:

创建枚举。最好使用大写字母表示值/常量。向客户实体添加新字段。使用 @ 注释 ne 字段。

@Entity
public class Customer {
    public enum Status { ACTIVATED, DEACTIVATED, SUSPENDED };
...
    @Enumerated(EnumType.ORDINAL)
    private Status status;
...
}

下图显示了数据库中为客户实体保存的记录

值从 0 开始。枚举中每个值的表示形式为

0 -> 激活

1 -> 停用

2 -> 暂停

让我们看看在我们的实体中使用 类型映射会发生什么

    @Enumerated(EnumType.STRING)
    private Status status;

现在表中的数据如下图所示

现在该专栏更容易解读。另一方面,存在重复。如果数据量很大,这可能会成为问题。

这两种开箱即用的解决方案效果很好Spring REST 与枚举一起使用Spring REST 与枚举一起使用,但也有局限性。如果我们想在表中使用不同的值,甚至不同的数据库类型(除 INT 或 之外)怎么办?

使用 @ 进行自定义映射

@ 注解将实体字段转换为数据库中的特定值。它需要一个 类来执行从实体属性状态到数据库列表示形式的转换,然后再返回。

对于我们的例子,该列中的值将如下所示: -> 1、 -> 2 和 -> 3。

此处描述了实现此目的的步骤:

将 code 属性添加到 枚举。这将是表列中的值。创建一个转换器类来进行对话。注释实体字段。

枚举中的更改很简单。

public enum Status {
    ACTIVATED(1), DEACTIVATED(2), SUSPENDED(3);
    int statusId;
    private Status(int statusId) {
        this.statusId = statusId;
    }
    public int getStatusId() {
        return statusId;
    }
};

转换器类必须实现 接口。它是一个通用接口,采用两个参数并声明两个方法。第一个类型是实体属性类型,第二个类型是将属性转换为的数据类型。

这里我们为 枚举创建转换器

@Converter
public class CustomerStatusConverter implements AttributeConverter {
    @Override
    public Integer convertToDatabaseColumn(Customer.Status status) 
   { 
        return status.getStatusId();
   }
    @Override
    public Customer.Status convertToEntityAttribute(Integer 
        statusId) {
        return Arrays.stream(Customer.Status.values())
                .filter(s -> s.getStatusId() == statusId)
                .findFirst()
                .orElseThrow(IllegalArgumentException::new);
    }
}

最后,实体属性用@注释,表示转换器类。

    @Convert(converter = CustomerStatusConverter.class)
    private Status status;

或者,注释 @ 可以采用属性 。它的默认值为 false,因此它被禁用。当它设置为 true 时, 将自动将转换器应用于第一个参数中指定类型的实体属性。

这就是所需要的一切。为了测试它,我们将使用 保留一些客户。

初始化客户表

我们将使用 用一些随机记录填充客户表。这是一个声明 run 方法的函数式接口。将在所有bean实例化并且应用程序上下文完全加载之后但在应用程序启动之前执行run方法。

@Configuration
public class DataInitializer implements CommandLineRunner {
    private CustomerRepo customerRepo;
    public DataInitializer(CustomerRepo customerRepo) {
        this.customerRepo = customerRepo;
    }
    @Override
    public void run(String... args) {       
        IntStream.rangeClosed(1,9)
            .forEach(this::createRandomCustomer);
    }
    public void createRandomCustomer(int id) {
        Customer customer = new Customer();
        customer.setId((long)id);
        customer.setName("name "+id+" surname "+id);
        customer.setEmail("organisation"+id+"@email.com");
        customer.setDateOfBirth(LocalDate.of(1980 + 2*id,id % 12, 
            (3*id) % 28));
        customer.setStatus(switch (id % 3) {
            case 0 -> Customer.Status.ACTIVATED;
            case 1 -> Customer.Status.DEACTIVATED;
            case 2 -> Customer.Status.SUSPENDED;
            default -> throw new IllegalStateException("Unexpected 
                       value: " + id % 3);
        });
        customerRepo.save(customer);
    }
}

现在表中有一些可用记录,其中 列中包含我们想要的值。

将 find 方法添加到存储库

移至 Web 层之前的最后一步是创建一个新方法java枚举有什么用,以在 类中按名称和状态进行过滤。 Data JPA 提供 @Query 注解来执行 JPQL 或本机 SQL。当 Data JPA 查询方法不提供搜索条件的解决方案时,这会派上用场。

我们希望在单个方法中按状态和名称进行搜索并忽略空值。然后,借助@Query我们就可以编写代码了

public interface CustomerRepo extends JpaRepository {
    @Query("""
        SELECT c FROM Customer c 
        WHERE (c.status = :status or :status is null) 
          and (c.name like :name or :name is null) """)
    public List findCustomerByStatusAndName(
            @Param("status") Customer.Status status,
            @Param("name") String name);
}

现在一切都已准备就绪,可以进入 Web 层了。

中的请求参数和枚举

这个想法是添加过滤器,以便 API 的客户端可以按姓名和状态搜索客户。过滤器将作为 GET HTTP 请求中的请求参数进行传递。名称可以接受任何字符串数据。相反,状态将仅限于枚举预定义值。

MVC 支持枚举作为参数以及路径变量。只需将参数放入控制器的方法签名中就足够了

@RestController
@RequestMapping("api/v1/customers")
public class CustomerController {
...
    @GetMapping
    public List findCustomers(
        @RequestParam(name="name", required=false) String name,
        @RequestParam(name="status", required=false) 
            Customer.Status status) {
        return customerRepo
            .findCustomerByStatusAndNameNamedParams(status, name)
            .stream()
            .map(CustomerUtils::convertToCustomerResponse)
            .collect(Collectors.toList());
   }
...
}

控制器已准备好接收参数。假设调用URL

:8080/api/v1/?= 。 将尝试将来自 http 参数 的值转换为有效的状态枚举。

在这种情况下,两个值都匹配:http 参数和状态枚举值。因此,API 返回的响应是激活客户的列表

[
    {
        "id": 3,
        "name": "name 3 surname 3",
        "email": "organisation3@email.com",
        "dateOfBirth": "09-03-1986",
        "status": "activated"
    },
    {
        "id": 6,
        "name": "name 6 surname 6",
        "email": "organisation6@email.com",
        "dateOfBirth": "18-06-1992",
        "status": "activated"
    },
    {
        "id": 9,
        "name": "name 9 surname 9",
        "email": "organisation9@email.com",
        "dateOfBirth": "27-09-1998",
        "status": "activated"
    }
] 

如果参数与任何枚举值不匹配或者无效,会发生什么情况?那么,如果它们不匹配或者无效java枚举有什么用, 将抛出 ption。

让我们通过以下调用来演示它。首先,URL 是

:8080/api/v1/?=

反应并不是我们所期望的

{
    "status": 400,
    "message": "There was a error converting the value activated 
                to type Status. ",
    "timestamp": "2023-06-24T19:33:26.6583667"
}

第二个 URL 为

:8080/api/v1/?=

在这种情况下,可以接受来自服务器的响应,因为状态不存在。

{
    "status": 400,
    "message": "There was a error converting the value DELETED to 
                type Status.",
    "timestamp": "2023-06-24T19:08:49.6261182"
}

出现此行为的原因是 使用

类。 方法委托给 Enum 方法,如果未找到枚举常量,该方法将抛出 tion。

@Nullable
public T convert(String source) {
    return source.isEmpty() ? null : Enum.valueOf(this.enumType, 
       source.trim());
}

我们希望改变这种行为并允许使用小写字母进行过滤。与 JPA 部分中所做的类似,我们必须编写一个自定义转换器。

创建 转换器

添加自定义转换器非常简单。只需实现 接口并重写其转换方法即可。 方法采用两种泛型类型:源类型和结果类型。

@Component
public class StringIgnoreCaseToEnumConverter implements 
   Converter {
    @Override
    public Customer.Status convert(String source) {
        return Customer.Status.valueOf(source.toUpperCase());        
    }
}

上面的方法接受传入的 。在通过 Enum. 返回枚举常量之前,它会被转换为大写。因此,字符串“”将变为“”。因此,它将返回枚举常量 .。

现在让我们重新测试之前的 URL 并检查结果。调用 URL

:8080/api/v1/?=会生成我们想要的输出

[
    {
        "id": 3,
        "name": "name 3 surname 3",
        "email": "organisation3@email.com",
        "dateOfBirth": "09-03-1986",
        "status": "activated"
    },
 ...
]

结论

本文详细讲解了如何在持久层和Web层使用枚举。这里总结一下它的要点。

对于持久层,JPA 提供了开箱即用的 和 选项。如果这不能满足应用程序的要求,自定义转换器可以完全控制映射的完成方式。

对于 Web 层, MVC 为请求参数和路径变量提供了枚举支持。如果存在精确匹配,它会自动将 转换为枚举。可以通过实现转换器来更改此行为。

关注并回复1领取Java学习资料包!

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » Spring REST 与枚举一起使用

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们