使用hibernate jpa进行JSON序列化和反序列化,以便在JSON响应中将父对象转换为子对象

时间:2017-10-11 08:58:15

标签: java json spring hibernate jackson

我正在使用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问题

要求:

  1. 我急需双方取得(大学方面好的)

  2. 每个学生都需要学生方面的大学对象(当我急切地拿学生时)

  3. 我需要使用哪种序列化或JSON配置来匹配我想要的响应?

    更新2:

    删除 @JsonIdentityInfo 并编辑学生方面如下:

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "university_id",updatable = false,insertable = false)
    @JsonIgnoreProperties(value = "students", allowSetters = true)
    private University university;
    

    json的反应仍然相同 我需要上面提到的期望的响应。

    感谢

8 个答案:

答案 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

  1. 删除@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;
        }
    }
    
  2. 向学生添加@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
    }
    
  3. 如果您需要在其他上下文中序列化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。