【问题标题】:Java REST endpoint to return any database columns用于返回任何数据库列的 Java REST 端点
【发布时间】:2019-02-25 01:21:24
【问题描述】:

假设我有一个名为 Employee 的 postgres 表,其中包含以下列:

  • 身份证
  • 名字
  • 姓氏
  • 就业
  • 日期
  • 经理
  • 部门

我有兴趣拥有一个 REST 端点,这样/employee/{ID} 将以 JSON 格式返回该特定员工的所有信息,但如果我指定 /employee/{ID}/FirstName,那么它将仅以 JSON 格式返回特定员工的名字,@ 987654323@ 将以 JSON 格式返回员工的姓氏,依此类推。有没有一种很好的方法来实现这一点,而不是实现一个端点来访问每一列?谢谢。

【问题讨论】:

  • 你不能用一个端点来做吗,即。 /employee/{id}/{attribute} 并将 attribute 映射到列名?
  • 我相信这是我正在寻找的解决方案。但这是否也意味着在我的端点函数中我必须有一个映射函数从属性中检索列?我想我的主要问题是,现在数据库中有 7 列,我可以按照您提到的那样定义它,但是如果将来添加第 8 列,我们是否可以创建端点现在这样我们就不必在添加新列时更改代码了?
  • 当我在端点中使用{attribute} 时,它是一个通配符——通常使用PathVariable(或等效项)实现。确保您用于 REST / MVC 的任何库都支持这一点。如果是这样,那么您将需要编写映射。如果您添加新列,映射将需要更改,但您的端点不会。
  • 谢谢 我相信“如果添加新列,映射将需要更改”是我真正想要验证的内容。

标签: java database rest


【解决方案1】:

解决此问题的一种简单方法是使用请求参数而不是查询 URL。使用fields 之类的参数,您将拥有/employee/{id}?fields=FirstName,LastName 之类的URL。使用下面的代码,您可以拥有一个Map<String, Object>,它将与您的数据一起序列化为 JSON。像这样:

@ResponseBody
public Map<String, Object> getPerson(@PathVariable("id") long id, @RequestParam("fields") String fields) throws Exception {
    return personService.getPersonFields(id, fields);
}

@Service
class PersonService {

    public Map<String, Object> getPersonFields(Long personId, String fields) throws Exception {
        final Person person = personRepository.findById(personId);
        if (person == null) {
            throw new Exception("Person does not exists!");
        }

        String[] fieldsArray = fields.split(",");
        Map<String, Field> personFields = Arrays.stream(person.getClass().getFields()).collect(Collectors.toMap(Field::getName, field -> field);

        Map<String, Object> personFieldsReturn = new HashMap<>();
        for (String field : fieldsArray) {
            if (personFields.containsKey(field)) {
                personFields.get(field).setAccessible(true);
                personFieldsReturn.put(field, personFields.get(field).get(person));
                personFields.get(field).setAccessible(false);
            }
        }

        return personFieldsReturn;
    }

}

虽然这不是一个好的解决方案。但它应该可以工作。

【讨论】:

  • 这是一个有风险的解决方案——最好不要在 API 上公开列名。
  • 好吧,使用它将您的 DTO 映射到控制器,而不是将实体映射到服务。也是一样的道理。
  • 这样你就可以定义一个抽象的控制器并集中所有DTO的映射字段的逻辑。你明白我的意思吗?
  • 我明白你的意思。但是,数据库列(或字段)属于数据访问层,不应出现在控制器中。实体属性(模型的一部分)到数据库概念之间的任何映射都属于数据访问层。
【解决方案2】:

正如@dave 在评论中提到的,您可以拥有一个 REST 端点 /employee/{ID}/{column},并且在您的控制器中,您将拥有 {column} 参数的值和数据库中的实际列名之间的映射。如果您不想在映射更改时重新部署应用程序,您可以将其放在 jar/war 之外的服务器上的单独属性文件中,您还可以添加一个额外的端点来重新加载服务器上的映射表单文件或端点这将允许上传和解析文件,并直接映射到您的应用程序。

【讨论】:

    【解决方案3】:

    我建议您使用Spring Data 中的RepositoryRestResource

    首先,创建你的实体:

    public class Employee {
        //props
    }
    

    然后创建Employee Repository:

    @RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
    public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
    
        List<Employee> findByLastName(@Param("firstName") String firstName);
    
    }
    

    仅此而已,您将获得:

    • 使用 HAL 作为媒体类型的域模型的可发现 REST API。
    • 代表您的模式的集合、项目和关联资源。
    • 分页和排序

    等等。

    查看文档:

    Spring Guide

    Spring Dc

    Spring Data Rest Project

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-30
      • 1970-01-01
      • 2019-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-07
      相关资源
      最近更新 更多