Jackson JSON包在從物件對映到Json字串過程的迴圈依賴問題-分析與解決

bladestone發表於2019-03-19

提出問題

在基於Jackson類庫將物件轉化為json字串時,如果物件中存在互相依賴,則會產生無限迴圈的情況,具體情況如下:
BuyerEntity.java

@Table(name="t_buyer")
@Entity
@ToString(callSuper=true)
@Data
@EqualsAndHashCode(callSuper=false)
public class BuyerEntity extends BaseEntity {
    @Column(nullable=false)
    private String name;
    
    @Column
    private Integer age;
    
    @Column(name="sex")
    private Integer sex;
    
    //@Enumerated(EnumType.ORDINAL)
    @Column(nullable=false, name="gender")
    @Convert(converter = GenderConverter.class)
    private Gender gender = Gender.Male;
    
    @Column
    private String email;
    
    @OneToMany(cascade= {CascadeType.ALL}, mappedBy="buyer")
    private List<OrderEntity> orders;
}

OrderEntity.java

@Entity
@Table(name="t_order")
@ToString(callSuper=true)
@Data
public class OrderEntity extends BaseEntity {
     
	//商品消費資料量
	@Column
	private int count;
	
	//消費記錄的總價
	@Column(name="total_fee")
	private double totalFee;
	
	//折扣率
	@Column(name="discount_rate")
	private double discountRate;
	
    @ManyToOne(cascade= {CascadeType.REFRESH})
	@JoinColumn(name="buyer_id", referencedColumnName="id")
	private BuyerEntity buyer;
}

生成Json字串的過程如下:

  BuyerEntity buyer = new BuyerEntity();
    	buyer.setAge(12);
    	buyer.setCreatedTime(new Date());
    	buyer.setUpdatedTime(new Date());
    	buyer.setEmail("123@123.cn");
    	buyer.setGender(Gender.Female);
    	buyer.setName("zhangsan");
    	
    	OrderEntity order1 = new OrderEntity();
    	order1.setCount(12);
    	order1.setCreatedTime(new Date());
    	order1.setUpdatedTime(new Date());
    	order1.setTotalFee(123.3);
    	order1.setDiscountRate(0.80);
    	order1.setBuyer(buyer);
    	
    	OrderEntity order2 = new OrderEntity();
    	order2.setCount(9);
    	order2.setCreatedTime(new Date());
    	order2.setUpdatedTime(new Date());
    	order2.setTotalFee(35.3);
    	order2.setDiscountRate(0.60);
    	order1.setBuyer(buyer);
    	
    	List<OrderEntity> orders = Lists.newArrayList();
    	orders.add(order1);
    	orders.add(order2);
    	
    	buyer.setOrders(orders);
    	
  ObjectMapper objectMapper = new ObjectMapper();
  String jsonStr = objectMapper.writeValueAsString(buyer);
  System.out.println(jsonStr);

上述結果資訊中則會出現如下的無限迴圈:

{"code":0,"message":"success","data":{"id":2,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"name":"zhangsan","age":12,"sex":null,"gender":"Female","email":"123@123.cn","orders":[{"id":3,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"count":12,"totalFee":123.3,"discountRate":0.8,"buyer":{"id":2,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"name":"zhangsan","age":12,"sex":null,"gender":"Female","email":"123@123.cn","orders":[{"id":3,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"count":12,"totalFee":123.3,"discountRate":0.8,"buyer":{"id":2,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"name":"zhangsan","age":12,"sex":null,"gender":"Female","email":"123@123.cn","orders":[{"id":3,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"count":12,"totalFee":123.3,"discountRate":0.8,"buyer":{"id":2,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"name":"zhangsan","age":12,"sex":null,"gender":"Female","email":"123@123.cn","orders":[{"id":3,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"count":12,"totalFee":123.3,"discountRate":0.8,"buyer":{"id":2,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"name":"zhangsan","age":12,"sex":null,"gender":"Female","email":"123@123.cn","orders":[{"id":3,"createdTime":"2019-03-19T01:30:53.123+0000","updatedTime":"2019-03-19T01:30:53.123+0000","version":0,"count":12,"totalFee":123.3,"discountRate":0.8,"

問題解決

那該問題該如何破解呢?

  1. 使用 @JsonManagedReference @JsonBackReference
    @JsonManagedReference: 正常輸出Json的字串
    @JsonBackReference: 則會忽略輸出json的字串
  2. @JsonIgnore: 其本身是忽略json的字串輸出的。

解決方案

於是將@JsonManagedReference 放到BuyerEntity.java中的List之上。

@JsonBackReference
  @ManyToOne(cascade= {CascadeType.REFRESH})
  @JoinColumn(name="buyer_id", referencedColumnName="id")
  private BuyerEntity buyer;

與之相對應的是另外一個:

@JsonManagedReference
  @OneToMany(cascade= {CascadeType.ALL}, mappedBy="buyer")
  private List<OrderEntity> orders;

測試用例

public class JacksonCircularReferenceTest {
    private ObjectMapper objectMapper = new ObjectMapper();
    
	@Before
	public void setUp() throws Exception {
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void test() throws JsonProcessingException {    
		BuyerEntity buyer = new BuyerEntity();
    	buyer.setAge(12);
    	buyer.setCreatedTime(new Date());
    	buyer.setUpdatedTime(new Date());
    	buyer.setEmail("123@123.cn");
    	buyer.setGender(Gender.Female);
    	buyer.setName("zhangsan");
    	
    	OrderEntity order1 = new OrderEntity();
    	order1.setCount(12);
    	order1.setCreatedTime(new Date());
    	order1.setUpdatedTime(new Date());
    	order1.setTotalFee(123.3);
    	order1.setDiscountRate(0.80);
    	order1.setBuyer(buyer);
    	
    	OrderEntity order2 = new OrderEntity();
    	order2.setCount(9);
    	order2.setCreatedTime(new Date());
    	order2.setUpdatedTime(new Date());
    	order2.setTotalFee(35.3);
    	order2.setDiscountRate(0.60);
    	order1.setBuyer(buyer);
    	
    	List<OrderEntity> orders = Lists.newArrayList();
    	orders.add(order1);
    	orders.add(order2);
    	
    	buyer.setOrders(orders);
    	
    	String jsonStr = objectMapper.writeValueAsString(buyer);
    	System.out.println(jsonStr);
	}
}

參考資料

  1. https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

相關文章