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

Why does Jackson's PropertyGenerator prevent recursive loop

我有两个双向映射的实体。具有注册集合和注册本身的车辆。这些实体作为 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, 属性 = "id") 到 Vehicle class 来解决问题。

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

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

实际发生的是 Jackson 正在序列化一个车辆,这会触发注册列表的序列化,这会序列化一个具有注册列表的车辆,等等。

如您所见,@JsonIdentityInfo 注释使无限递归短路,但这并不是因为您指定的特定 PropertyGenerator@JsonIdentityInfo 的 JavaDoc 提供了一些关于正在发生的事情的见解:

@JsonIdentityInfo Annotation used for indicating that values of annotated type or property should be serializing so that instances either contain additional object identifier (in addition actual object properties), or as a reference that consists of an object id that refers to a full serialization. In practice this is done by serializing the first instance as full object and object identity, and other references to the object as reference values.

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

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

现在,针对您关于 PropertyGenerator 实际作用的具体问题:它指示 Jackson 使用您的 class 中的特定 属性 作为完整序列化版本的参考物体。您将 "id" 指定为 属性,因此在生成的序列化 JSON/XML 中,您应该看到,如果您有一个 ID 为 342 的对象,那么第一个序列化对象将显示该 ID,所有其他序列化对象属性,但随后每次对该对象进行序列化时,您只会看到 342 序列化为对原始对象的引用。