为什么Jackson的PropertyGenerator会阻止递归循环

时间:2015-10-26 09:56:24

标签: java json rest jackson jax-rs

我有两个双向映射的实体。一种具有注册集合和注册本身的车辆。这些实体作为REST服务公开。

@Entity
@XmlRootElement
public class Vehicle implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String brand;
  private String type;

  @OneToMany(mappedBy = "vehicle", fetch = FetchType.EAGER)
  private List<Registration> registrations;
}

问题是FetchType.EAGER产生无限递归

@Entity
@XmlRootElement
public class Registration implements Serializable {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String name;

  @ManyToOne
  private Vehicle vehicle;
}

经过一番研究后我发现我必须将注释 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property =&#34; id&#34;)添加到Vehicle类中解决这个问题。

我的问题是:PropertyGenerator类实际上做了什么?

1 个答案:

答案 0 :(得分:4)

问题不在于FetchType.EAGERFetchType.EAGER是JPA注释,在使用Jackson序列化Vehicle实例时不会发挥作用。

实际发生的事情是杰克逊正在序列化一个车辆,该车辆会触发登记列表的序列化,该车辆序列化车辆,其中包含注册列表,等等。

正如您所注意到的,@JsonIdentityInfo注释会使无限递归短路,但这并不是因为您指定的特定PropertyGenerator@JsonIdentityInfo的JavaDoc可以让您深入了解正在发生的事情:

  

@JsonIdentityInfo 用于指示带注释的类型或属性的值应该序列化的注释,以便实例包含其他对象标识符(此外还包含实际对象属性),或者作为包含其的参考引用完整序列化的对象ID。实际上,这是通过将第一个实例序列化为完整对象和对象标识以及将对象作为参考值的其他引用来完成的。

换句话说,第一次序列化时,您将获得完整的序列化JSON / XML版本。下次该对象被序列化时,您将获得对该第一个序列化对象的引用。

还有其他方法可以解决这个问题。查看Infinite Recursion with Jackson JSON and Hibernate JPA issue(您的问题可以说与此问题相同)。这里的答案建议您也可以使用@JsonIgnore@JsonManagedReference@JsonBackReference的组合来避免无限递归。

现在,关于PropertyGenerator实际做什么的具体问题:它指示Jackson使用您班级中的特定属性作为对象的完整序列化版本的引用。您将“id”指定为属性,因此在生成的序列化JSON / XML中,您应该看到如果您有一个ID为342的对象,那么第一个序列化对象将显示id和所有其他序列化属性,但每次后续该对象都是序列化后,您将看到342序列化为对原始对象的引用。