使用休眠 jpa 进行 JSON 序列化和反序列化以在 JSON 响应中将父对象转换为子对象
Posted
技术标签:
【中文标题】使用休眠 jpa 进行 JSON 序列化和反序列化以在 JSON 响应中将父对象转换为子对象【英文标题】:JSON serializing and deserializing with hibernate jpa to have parent object into child in JSON response 【发布时间】:2018-03-22 21:08:04 【问题描述】:我正在使用 Spring 框架、Hibernate 和 JSON 开发 REST Web 应用程序。请假设我有两个如下实体:
BaseEntity.java
@MappedSuperclass
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id" )
public abstract class BaseEntity implements Serializable
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
public long getId()
return id;
大学.java
public class University extends BaseEntity
private String uniName;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval = true)
@JoinColumn(name = "university_id")
private List<Student> students=new ArrayList<>();
// setter an getter
学生.java
public class Student extends BaseEntity
private String stuName;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "university_id",updatable = false,insertable = false)
private University university;
// setter an getter
当我调用我的 rest api 来列出大学时,一切都按我的预期正常工作,但是当我调用我的 rest api 来热切地列出学生时,我的 JSON 响应是
[
"id": 1,
"stuName": "st1",
"university":
"id": 1,
"uniName": "uni1"
,
"id": 2,
"stuName": "st2",
"university": 1
]
但我的期望回应是:
[
"id": 1,
"stutName": "st1",
"university":
"id": 1,
"uniName": "uni1"
,
"id": 2,
"stutName": "st2",
"university":
"id": 1,
"uniName": "uni1"
更新 1:我的休眠注释工作正常我有 JSON 问题
要求:
双方都急需取货(大学这边可以)
我需要每个学生在学生方面的大学对象(当我急切地获取学生时)
我需要什么样的序列化或 JSON 配置来匹配我想要的响应?
更新 2:
通过删除 @JsonIdentityInfo 并编辑学生端,如下所示:
@ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "university_id",updatable = false,insertable = false) @JsonIgnoreProperties(value = "students", allowSetters = true) private University university;
json 响应还是一样的 我需要上面提到的期望回复。
谢谢
【问题讨论】:
添加@JsonIgnore
并尝试
@Hema 我需要在学生端有大学对象而不是大学ID
是的,我提供了相同的代码。你将在 Student 上大学
@Generic 不要以为可以用简单的通用方式来完成。你认为list University is Ok
因为那里没有重复项。 @JsonIdentityInfo
ID/reference 机制的工作原理是,一个对象实例只被完全序列化一次,并由它的 ID 在其他地方引用 github.com/FasterXML/jackson-databind/issues/372
你能查一下***.com/a/22617567/3530898
【参考方案1】:
从基类中删除 @JsonIdentityInfo
,这会导致大学对象仅序列化 id。
【讨论】:
我需要@JsonIdentityInfo 来消除循环异常 我认为自定义序列化程序可以完成这项工作 在我的应用程序中有大量对象怎么可能!我需要为每一个编写序列化程序吗?【参考方案2】:你也可以将@JoinColumn 添加到 Student 实体吗
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = student_id")
还要检查你的大学实体类的外键。外键应该来自其他实体吧? @JoinColumn(name = "university_id",foreignKey = @ForeignKey(name = "student_id")) ??
另外,您也可以使用“mappedBy”。
@JoinColumn(name = "university_id", mappedBy="university")
private List<Student> students=new ArrayList<>();
【讨论】:
这些变化对你的想法中的 jason 输出有影响吗? 我的 json 输出有问题【参考方案3】:你可以添加这个并检查
大学
public class University
@Fetch(value = FetchMode.SELECT)
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "university_id")
@JsonIgnore
private List<Student> students;
学生
public class Student
@ManyToOne
@JoinColumn(name = "university_id", insertable = true, updatable = true, nullable = true)
private University university;
【讨论】:
我试过了。它返回 [ "id": 1, "stutName": "st1", "university": "id": 1, "uniName": "uni1" , "id": 2, "stutName": "st2", "大学": 1 ] 它为第二个学生添加 uni id 而不是 uni 对象 第一个对象,你得到想要的格式对吗...? 如果您正确获取第一个对象格式,则映射工作正常 现在问题可能出在your values saved in DB
正确检查一次【参考方案4】:
我了解您不想在 JSON 中包含 University.students
。
删除@JsonIdentityInfo
@MappedSuperclass
//@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id" )
public abstract class BaseEntity implements Serializable
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
public long getId()
return id;
给学生加@JsonIgnore
避免圈子
public class University extends BaseEntity
private String uniName;
@JsonIgnore
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval = true)
@JoinColumn(name = "university_id",foreignKey = @ForeignKey(name = "university_id"))
private List<Student> students=new ArrayList<>();
// setter an getter
如果您需要在其他上下文中序列化 University.students
,请阅读 http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion。那里解释了处理双向关系的其他选项。
【讨论】:
通过添加@JsonIgnore,学生对象将在json输出中被忽略 你想序列化什么? List为 getter 方法添加 @jsonignore 并将@jsonProperty 添加到该字段 喜欢
@JsonProperty(access = Access.READ_ONLY)
private String password;
最近向 jackson 添加了一些功能,例如 Readonly 和 writeonly 你可以参考这个:http://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.Access.html
【讨论】:
我可以在哪里添加@JsonProperty(access = Access.READ_ONLY)?在大学二传手! 在学生类侧添加流派,如@JsonProperty(access = Access.READ_ONLY) 私立大学大学; 结果是一样的,没有任何区别【参考方案6】:您可能想尝试使用@JsonRawValue
作为university
属性的注释。您遇到的行为是由于引用崩溃 - 因为它两次相同 University
,所以序列化程序会尝试变得聪明,并在第二次遇到它时返回一个引用。
编辑:toString()
:
@Override
public String toString()
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
【讨论】:
在添加 @JsonRawValue 后,行格式的响应为 [ "id" : 1, "stuName" : "stu1", "university" : entity.University@57ab302c , "id" : 2, "stuName" : "stu2", "university" : entity.University@57ab302c ] 那么接下来的步骤是什么? 使用返回ObjectMapper
的 University
类覆盖 toString()
,返回 mapperObject.writeValueAsString(this)
- 这应该使它返回 JSON 而不是标准的 toString()
表示。
添加了toString()
。
它在 com.fasterxml.jackson.databind.ser.std.RawSerializer.serialize(RawSerializer.java:30) 在 com.fasterxml.jackson 产生错误 University.toString(University.java:46) .databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)【参考方案7】:
映射关系的方式,即使它“工作正常”,也不符合双向关系的 jpa 规范。请参阅this question 和相关答案。
一般来说,关系的所有者是多对一的一方,而一对多的一方是用mappedBy注解的。您的映射解决方案,拥有该关系的一对多方不是通常/推荐的(如上面的答案中所述),但在技术上是可行的。 (@manyToOne 在您的示例中遗漏了一些属性,例如“updatable=false”)
那么with JPA and recent Hibernate version,懒加载策略如下:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
因此,我建议您使用此默认延迟加载策略,并更改您的 manyToOne 关系的所有者,因为通过单个大学资源请求获取所有学生似乎不是一个好主意。 (你听说过分页吗?)
这样做,并从编组中排除学生集合,例如使用 @JsonIgnore,应该可以解决问题。
【讨论】:
亲爱的@Rémi Bantos 我已经尝试过使用该注释,没有 json 可以完美地工作,但是它们不能使用 json。如果您尝试添加与 Student 数据的 University 关系,它将无法在 student 表中添加 university_id。 在这种情况下,@JsonManagedReference 是必需的,我必须失去一些功能,例如我不能在 Json 响应中的 Student 端拥有 University 对象。 我已经详细说明了我的答案。你真的应该考虑改变你映射你的多对一一对多关系的方式,为学生提供适当的编组排除,如此处所述。因为,您映射的方式和杰克逊进行编组的方式是相关的。此外,我了解您希望包括具有急切获取类型的学生,我真的建议您不要这样做。相反,您应该更喜欢分页来获取所有这些内容。【参考方案8】:我遇到了同样的问题。 Hibernate(或 eclipselink)不是问题。 JPA 中唯一的约束是 FetchType.EAGER。
在 BaseEntity 中我添加了一个标准方法
public String getLabel()
return "id:"+this.getId();
这个方法是抽象的,但是我有很多类,我不想全部改变,所以我添加了一个默认值。
在父实体中,在本例中为大学,覆盖该方法
@Override
public String getLabel
return this.uniName;
对于每个父类,使用特定字段作为实体的标签
定义一个 MyStandardSerializer:
public class StandardJsonSerializer extends JsonSerializer<EntityInterface>
@Override
public void serializeWithType(EntityInterface value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException
serialize(value, jgen, provider);
@Override
public void serialize(EntityInterface value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException
jgen.writeStartObject();
jgen.writeNumberField("id", (long) value.getId());
jgen.writeStringField("label", value.getLabel());
jgen.writeEndObject();
在学生课上,在大学添加:
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "university_id",updatable = false,insertable = false)
@JsonSerialize(using=StandardJsonSerializer.class)
private University university;
现在您已经解决了循环问题。 当您需要标签时,覆盖父实体中的方法。 当您需要某些特定字段时,请创建特定的序列化程序。
【讨论】:
以上是关于使用休眠 jpa 进行 JSON 序列化和反序列化以在 JSON 响应中将父对象转换为子对象的主要内容,如果未能解决你的问题,请参考以下文章
Java下用Jackson进行JSON序列化和反序列化(转)