it-swarm.com.ru

Сериализация и десериализация JSON с помощью hibernate jpa для включения родительского объекта в child в ответе JSON

Я занимаюсь разработкой веб-приложения для отдыха с использованием Spring Framework, Hibernate и JSON. Пожалуйста, предположим, что у меня есть две сущности, как показано ниже:

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 rest, чтобы перечислить University, все работает нормально, как я ожидаю, но когда я вызываю api rest, чтобы перечислить студента, мой ответ 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

Требования :

  1. Мне нужно с обеих сторон получить с нетерпением (университетская сторона в порядке)

  2. Мне нужен университетский объект на стороне студента для каждого студента (когда я с нетерпением выбираю студента)

Какой тип сериализации или 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
Generic

Я понимаю, что вы не хотите включать University.students в свой JSON. 

  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
    }
    

Если вам нужно, чтобы University.students был сериализован в других контекстах, дайте http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion прочитайте. Там описаны другие варианты работы с двунаправленными отношениями.

1
Chris

То, как вы отображаете ваши отношения, даже если они «работают нормально», не соответствует спецификации jpa для двунаправленных отношений. Смотрите этот вопрос и соответствующие ответы.

Подводя итог, можно сказать, что владельцем отношения является сторона «многие к одному», а сторона «один ко многим» снабжена пометкой mappedBy. Ваше картографическое решение, при котором отношения один-ко-многим владеют отношениями, обычно/не рекомендуется (как описано в ответах выше), но технически возможно. (Сторона @manyToOne пропускает некоторые атрибуты, такие как "updatable = false" в вашем примере)

Затем, с JPA и последней версией Hibernate , политика отложенной загрузки выглядит следующим образом:

 OneToMany: LAZY
 ManyToOne: EAGER
 ManyToMany: LAZY
 OneToOne: EAGER

Поэтому я бы предложил вам использовать эту политику отложенной загрузки по умолчанию и сменить владельца ваших отношений manyToOne, так как не кажется хорошей идеей собрать всех студентов с помощью одного запроса ресурсов университета. (Вы слышали о нумерации страниц?)

Выполнение этого, а также исключение коллекции студентов из Marshalling с использованием, например, @JsonIgnore, должно помочь.

1
Rémi Bantos

Добавьте @jsonignore для метода получения
и добавьте @jsonProperty к полю like 

@JsonProperty(access = Access.READ_ONLY)
private String password;

Недавно добавил к Джексону некоторые функции, такие как Readonly и writeonly
вы можете сослаться на это:
http://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.Access.html

1
vishal paalakurthi

Вы можете добавить это и проверить

Университет

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;
}
1
Hema

Можете ли вы также добавить @JoinColumn в сущность Student

@ManyToOne(fetch = FetchType.EAGER)  
@JoinColumn(name = student_id")

Также проверьте внешний ключ вашего класса сущностей вашего университета. Внешний ключ должен быть от другой сущности, верно?

В противном случае вы также можете использовать «mappedBy».

@JoinColumn(name = "university_id", mappedBy="university")
        private List<Student> students=new ArrayList<>();
1
Tharsan Sivakumar

Удалите @JsonIdentityInfo из базового класса, это заставит университетский объект сериализовать только id.

1
Sachin Gupta

Возможно, вы захотите использовать @JsonRawValue в качестве аннотации для своего свойства university. Поведение, с которым вы сталкиваетесь, связано со свертыванием ссылок - так как он один и тот же University дважды, сериализатор пытается быть умным и просто возвращает ссылку во второй раз, когда встречается.

Правка: toString():

@Override
public String toString() {
    ObjectMapper mapper = new ObjectMapper();
    return mapper.writeValueAsString(this);
}
1
Piotr Wilkin

У меня такая же проблема. 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;

Теперь вы решили круглость. Когда вам нужна метка, переопределите метод в родительской сущности . Когда вам нужны некоторые конкретные поля, создайте определенный сериализатор.

1
Daniele Licitra