我有两个双向映射的实体。具有注册集合和注册本身的车辆。这些实体作为 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 = "id") 添加到 Vehicle 类才能解决问题。
我的问题是:PropertyGenerator 类实际上做了什么?
最佳答案
问题不是因为 FetchType.EAGER
。 FetchType.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 使用您类中的特定属性作为对象的完整序列化版本的引用。您将“id”指定为属性,因此在生成的序列化 JSON/XML 中您应该看到,如果您有一个 ID 为 342 的对象,那么第一个序列化对象将显示 id 和所有其他序列化属性,但随后每次该对象都是序列化后,您只会看到序列化为对原始对象的引用的 342。
关于java - 为什么 Jackson 的 PropertyGenerator 会阻止递归循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33342827/