【问题标题】:Spring: one JPA model, many JSON respresentationsSpring:一个 JPA 模型,许多 JSON 表示
【发布时间】:2015-02-11 06:31:32
【问题描述】:

我正在使用 Spring/JPA 编写一个 RESTful Web 服务。有一个通过 Web 服务公开的 JPA 模型。 “课程”模型相当宽泛——它实际上由几组数据组成:一般信息、定价细节和一些缓存。

我遇到的问题是无法使用相同的 JPA 模型发布不同的 JSON 表示。

第一种情况我只需要返回general_info课程的数据集:

GET /api/courses/general_info

在第二种情况下,我只想返回 pricing 数据集:

GET /api/courses/pricing

我看到了以下方法来解决这个问题,不是特别的顺序:

  1. 使用创建 CourseGeneralInfoCoursePricing JPA 模型 原始数据库表作为源。 CourseGeneralInfo模特 会有自己的一组字段,CoursePricing 会有自己的 自己的。这样我就有了我需要的 JSON。

  2. 重构课程模型/表中的内容 GeneralInfo 和 PricingDetails 是单独的 JPA 实体。好的,这听起来是最好的(imo),虽然数据库是遗留的,而且我不能轻易更改...

  3. 利用某种 DTO 和 Spring Mappers 将 JPA 模型转换为任何特定情况下所需的表示。

你会推荐什么方法?

【问题讨论】:

    标签: spring jpa


    【解决方案1】:

    我刚刚阅读了 Spring 4.1 中一些非常棒的特性,它们允许您通过注释使用不同的视图。

    来自:https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

    public class View {
        interface Summary {}
    }
    
    public class User {
    
        @JsonView(View.Summary.class)
        private Long id;
    
        @JsonView(View.Summary.class)
        private String firstname;
    
        @JsonView(View.Summary.class)
        private String lastname;
    
        private String email;
        private String address;
        private String postalCode;
        private String city;
        private String country;
    }
    
    public class Message {
    
        @JsonView(View.Summary.class)
        private Long id;
    
        @JsonView(View.Summary.class)
        private LocalDate created;
    
        @JsonView(View.Summary.class)
        private String title;
    
        @JsonView(View.Summary.class)
        private User author;
    
        private List<User> recipients;
    
        private String body;
    }
    

    感谢 Spring MVC @JsonView 支持,可以根据每个处理程序方法选择应该序列化的字段:

    @RestController
    public class MessageController {
    
        @Autowired
        private MessageService messageService;
    
        @JsonView(View.Summary.class)
        @RequestMapping("/")
        public List<Message> getAllMessages() {
            return messageService.getAll();
        }
    
        @RequestMapping("/{id}")
        public Message getMessage(@PathVariable Long id) {
            return messageService.get(id);
        }
    }
    

    在此示例中,如果检索到所有消息,则只有最重要的字段会被序列化,这要归功于带有 @JsonView(View.Summary.class) 注释的 getAllMessages() 方法:

    [ {
      "id" : 1,
      "created" : "2014-11-14",
      "title" : "Info",
      "author" : {
        "id" : 1,
        "firstname" : "Brian",
        "lastname" : "Clozel"
      }
    }, {
      "id" : 2,
      "created" : "2014-11-14",
      "title" : "Warning",
      "author" : {
        "id" : 2,
        "firstname" : "Stéphane",
        "lastname" : "Nicoll"
      }
    }, {
      "id" : 3,
      "created" : "2014-11-14",
      "title" : "Alert",
      "author" : {
        "id" : 3,
        "firstname" : "Rossen",
        "lastname" : "Stoyanchev"
      }
    } ]
    

    在 Spring MVC 默认配置中,MapperFeature.DEFAULT_VIEW_INCLUSION 设置为 false。这意味着当启用 JSON 视图时,非注释字段或属性(如正文或收件人)不会被序列化。

    当使用 getMessage() 处理程序方法(未指定 JSON 视图)检索特定消息时,所有字段都按预期序列化:

    {
      "id" : 1,
      "created" : "2014-11-14",
      "title" : "Info",
      "body" : "This is an information message",
      "author" : {
        "id" : 1,
        "firstname" : "Brian",
        "lastname" : "Clozel",
        "email" : "bclozel@pivotal.io",
        "address" : "1 Jaures street",
        "postalCode" : "69003",
        "city" : "Lyon",
        "country" : "France"
      },
      "recipients" : [ {
        "id" : 2,
        "firstname" : "Stéphane",
        "lastname" : "Nicoll",
        "email" : "snicoll@pivotal.io",
        "address" : "42 Obama street",
        "postalCode" : "1000",
        "city" : "Brussel",
        "country" : "Belgium"
      }, {
        "id" : 3,
        "firstname" : "Rossen",
        "lastname" : "Stoyanchev",
        "email" : "rstoyanchev@pivotal.io",
        "address" : "3 Warren street",
        "postalCode" : "10011",
        "city" : "New York",
        "country" : "USA"
      } ]
    }
    

    @JsonView 注解只能指定一个类或接口,但您可以使用继承来表示 JSON 视图层次结构(如果字段是 JSON 视图的一部分,它也将是父视图的一部分)。例如,此处理程序方法将序列化带有@JsonView(View.Summary.class)@JsonView(View.SummaryWithRecipients.class) 注释的字段:

    public class View {
        interface Summary {}
        interface SummaryWithRecipients extends Summary {}
    }
    
    public class Message {
    
        @JsonView(View.Summary.class)
        private Long id;
    
        @JsonView(View.Summary.class)
        private LocalDate created;
    
        @JsonView(View.Summary.class)
        private String title;
    
        @JsonView(View.Summary.class)
        private User author;
    
        @JsonView(View.SummaryWithRecipients.class)
        private List<User> recipients;
    
        private String body;
    }
    
    @RestController
    public class MessageController {
    
        @Autowired
        private MessageService messageService;
    
        @JsonView(View.SummaryWithRecipients.class)
        @RequestMapping("/with-recipients")
        public List<Message> getAllMessagesWithRecipients() {
            return messageService.getAll();
        }
    }
    

    【讨论】:

      【解决方案2】:

      在 Spring Data REST 2.1 中,为此目的提供了一种新机制 - 投影(它现在是 spring-data-commons 的一部分)。

      您需要定义接口,包含完全公开的字段:

      @Projection(name = "summary", types = Course.class)
      interface CourseGeneralInfo {
      
        GeneralInfo getInfo();
      
      }
      

      之后,Spring 将能够在您的源代码中自动找到它,您可以向现有端点发出请求,如下所示:

      GET /api/courses?projection=general_info
      

      基于 https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra

      带有预测的 Spring 示例项目: https://github.com/spring-projects/spring-data-examples/tree/master/rest/projections

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-09-01
        • 2017-04-28
        • 2017-06-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-07
        • 2011-11-24
        相关资源
        最近更新 更多