忽略要在 JSON 响应中发送的父对象 - 循环问题 - JPA

Posted

技术标签:

【中文标题】忽略要在 JSON 响应中发送的父对象 - 循环问题 - JPA【英文标题】:Ignore Parent Object to be sent in the JSON response - Cyclic Problem - JPA 【发布时间】:2021-09-02 17:22:12 【问题描述】:

请耐心等待。尽我所能用简单的示例代码来解释。

两个实体 - ShopProduct

关系 - 一个 Shop 可以有多个 Product

我返回一个 Shop 对象,它一直像这样打印 -


    "shopId": 1,
    "shopName": "S1",
    "productList": [
        
            "productId": 100,
            "productName": "MOBILE",
            "shop": 
                "shopId": 1,
                "shopName": "S1",
                "productList": [
                    
                        "productId": 100,
                        "productName": "MOBILE",
                        "shop": 

在开始处理实际问题之前,我确实部分解决了循环问题但遇到了一个新问题。我在@JsonIgnore的帮助下停止了它

基本上,当我打印我的父 (Shop) json 对象时,我在子 (Product) 类字段中使用 @JsonIgnore 停止了循环响应。

@JsonIgnore 
private Shop shop

所以,现在 API 1 =

@GetMapping("/getShopById")
    public Shop getShopById()
        return shopRepo.findById(1L).get();
    

给我输出 - (这是完美的,因为我避免打印 Shop 回来);


    "shopId": 1,
    "shopName": "S1",
    "productList": [
        
            "productId": 100,
            "productName": "MOBILE"
        ,
        
            "productId": 101,
            "productName": "EARPHONE"
        
    ]


但是现在任何时候我想从Product 对象中获取Shop 并发送响应我得到一个错误,这是因为@JsonIgnore 我猜,这基本上完全停止了该字段的序列化来自Product 对象。

API 2 =

@GetMapping("/getShopFromTheProductId")
    public Shop getShopFromProductId() 
        Shop s = productRepo.findById(100L).get().getShop();
        return s;
    

给我错误 -


com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.doubt.***.Shop$HibernateProxy$YEW0qvzw["hibernateLazyInitializer"])
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty

总结一下,除非我明确要求,否则我如何才能忽略打印/从 Child 返回父级?

可能的解决方案 1 - 从 Product 实体中删除 Shop (private Shop getShop()) 的吸气剂。但这对我来说不是一个解决方案,因为当我在业务逻辑中可能需要它时,我将永远无法追踪到父级。

我的课-

控制器 -

@RestController
public class MainController 

    @Autowired
    private ShopRepo shopRepo;
    @Autowired
    private ProductRepo productRepo;
    
    @GetMapping("/getShopById")
    public Shop getShopById()
        return shopRepo.findById(1L).get();
    
    
    @GetMapping("/getShopFromTheProductId")
    public Shop getShopFromProductId() 
        Shop s = productRepo.findById(100L).get().getShop();
        return s;
    


店铺实体 -

@Entity
@Table(name = "SHOP")
public class Shop 

    @Id
    @Column(name = "SHOP_ID")
    private Long shopId;
    
    @Column(name = "SHOP_NAME")
    private String shopName;
    
    @OneToMany(fetch = FetchType.LAZY,targetEntity = Product.class, mappedBy = "shop")
    private List<Product> productList;
........

all the getters and setters

产品实体-

@Entity
@Table(name = "PRODUCT")
public class Product 

    @Id
    @Column(name = "PRODUCT_ID")
    private Long productId;
    
    @Column(name = "PRODUCT_NAME")
    private String productName;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "SHOP_ID")
    @JsonIgnore
    private Shop shop;
........
all getters and setters

【问题讨论】:

所以我已经接受了这个问题。首先,直接发送域对象作为响应是错误的。最佳实践是拥有一个 RequestShopDTO 对象和类似的 ResponseShopDTO。我们应该让它们具有与域对象相同的 getter 和 setter。附言- DTO - 数据传输对象 【参考方案1】:

为了避免循环问题使用@JsonManagedReference,@JsonBackReference 如下。

在父类、商店实体上添加@JsonManagedReference。

@JsonManagedReference
@OneToMany(fetch = FetchType.LAZY,targetEntity = 
Product.class, mappedBy = "shop")
private List<Product> productList;

在子类上添加@JsonBackReference,如下所示。

@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SHOP_ID")
@JsonIgnore
private Shop shop;

【讨论】:

不能解决问题。 @JsonBackReference@JsonIgnore 相同,只有差异是 @JsonBackReference 与子字段中的父字段一起使用。这只是一种更好的做法。所以根据你的解决方案。在尝试从子对象获取父对象时,我仍然遇到同样的问题(从 Product 获取 Shop 时仍然会出现相同的错误)。 JsonReference 的主要目的是避免循环问题。你用的是什么版本的杰克逊。你可以试试 2.9.8 版本。 (您的 jar 似乎缺少一些库) 使用2.12版本的jackson 升级到2.9.8等新版本 @S.Anushan 您知道语义版本控制的工作原理吗? 2.12是较新的版本【参考方案2】:

所以我已经接受了这个问题。 首先,直接发送域对象作为响应是错误的。根本不是最佳实践。 最佳实践是有一个RequestShopDTO 对象,类似地和ResponseShopDTO。我们应该拥有与域对象相同的 getter 和 setter 的 DTO,在本例中为 Shop

    Rest API 应该接收RequestShopDTO 对象。 使用工厂类/适配器类将 RequestShopDTO 转换为 Shop Domain 对象并将其转发到业务层。 同样,我们应该将 Response Shop 域对象转换为 ResponseShopDTO 对象并将其作为响应发送。 我们应该有类似 BaseRequest 类,由 CreateRequest、UpdateRequest、GetRequest 等扩展...其中所有 get 请求共有的属性在 GetRequest 中,然后由更具体的请求类(如 RequestShopDTO)扩展。 类似地,我们可以有一个像 RequestDtoToToDomainBaseAdapter 这样的抽象适配器类,它被类似的东西扩展 ShopDtoToShopDomainAdapter。

参考 - inor 的回答 - Design Pattern to model Request and Response Objects for Webservices 附言- DTO - 数据传输对象

【讨论】:

以上是关于忽略要在 JSON 响应中发送的父对象 - 循环问题 - JPA的主要内容,如果未能解决你的问题,请参考以下文章

ServiceStack Json Serializer 忽略属性

在从 Spring MVC 作为 JSON 发送时动态忽略 Java 对象中的字段

Express响应方法

PHP 循环遍历嵌套的 JSON 响应并重新组装为 Webhook 的简单查询字符串

将字段注入 JSON 响应对象

忽略要在 git 中提交的文件夹