我正在使用spring框架,Hibernate和JSON开发rest web app。请假设我有两个如下实体:
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;
}
}
University.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
}
Student.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
}
当我打电话给我的休息api列出大学时,每件事情都按照我的预期正常工作,但是当我打电话给我的休息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 :我的hibernate注释工作正常我有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的反应仍然相同 我需要上面提到的期望的响应。
感谢
答案 0 :(得分:1)
从基类中删除@JsonIdentityInfo
,这导致大学对象仅序列化id。
答案 1 :(得分:1)
您是否可以将@JoinColumn添加到学生实体
@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<>();
答案 2 :(得分:1)
您可以添加此项并检查
<强>大学强>
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;
}
答案 3 :(得分:1)
我们了解到您不希望在您的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。其中解释了处理双向关系的其他选项。
答案 4 :(得分:1)
映射关系表的方式,即使它“工作正常”,也不符合双向关系的jpa规范。请参阅this question及相关答案。
通常总结一下,关系的所有者是多对一方,而一对多方是用mappedBy注释的。您的映射解决方案,一对多方拥有关系是不常见/推荐(如上面的答案所述),但技术上可行。 (@manyToOne方面错过了一些属性,比如你的例子中的“updatable = false”)
然后,with JPA and recent Hibernate version,延迟加载策略如下:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
因此我建议您使用此默认延迟加载策略,并更改manyToOne关系的所有者,因为通过单个大学资源请求获取所有学生似乎不是一个好主意。 (你听说过分页吗?)
这样做,并且使用例如 @JsonIgnore 排除编组中的学生集合应该可以解决问题。
答案 5 :(得分:1)
为getter方法添加@jsonignore
并将@jsonProperty添加到该字段
喜欢
@JsonProperty(access = Access.READ_ONLY)
private String password;
最近为Jackon添加了一些功能,如Readonly和writeonly
你可以参考:
http://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.Access.html
答案 6 :(得分:1)
您可能希望尝试使用@JsonRawValue
作为university
媒体资源的注释。您遇到的行为是由于引用折叠 - 因为它是两次相同的University
,序列化程序会尝试变聪明,并在第二次遇到时返回引用。
编辑:toString()
:
@Override
public String toString() {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
答案 7 :(得分:1)
我遇到了同样的问题。 Hibernate(或eclipselink)不是问题。 JPA中唯一的限制是FetchType.EAGER 。
在BaseEntity中我添加了一个标准方法
public String getLabel(){
return "id:"+this.getId();
}
这个方法是抽象的,但我有很多课程,我不想改变它所以我添加了一个默认值。
在父实体中,在本例中为University,重写方法
@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;
现在你已经解决了循环问题。 需要标签时,覆盖父实体中的方法。 当您需要某些特定字段时,请创建特定的Serializer。