복붙노트

[SPRING] 스프링 부트 @ResponseBody가 엔티티 ID를 직렬화하지 않습니다.

SPRING

스프링 부트 @ResponseBody가 엔티티 ID를 직렬화하지 않습니다.

이상한 문제가있어서 그것을 처리하는 방법을 알 수 없습니다. 단순한 POJO :

@Entity
@Table(name = "persons")
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "middle_name")
    private String middleName;

    @Column(name = "last_name")
    private String lastName;

    @Column(name = "comment")
    private String comment;

    @Column(name = "created")
    private Date created;

    @Column(name = "updated")
    private Date updated;

    @PrePersist
    protected void onCreate() {
        created = new Date();
    }

    @PreUpdate
    protected void onUpdate() {
        updated = new Date();
    }

    @Valid
    @OrderBy("id")
    @OneToMany(mappedBy = "person", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<PhoneNumber> phoneNumbers = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Date getCreated() {
        return created;
    }

    public Date getUpdated() {
        return updated;
    }

    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void addPhoneNumber(PhoneNumber number) {
        number.setPerson(this);
        phoneNumbers.add(number);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

@Entity
@Table(name = "phone_numbers")
public class PhoneNumber {

    public PhoneNumber() {}

    public PhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "phone_number")
    private String phoneNumber;

    @ManyToOne
    @JoinColumn(name = "person_id")
    private Person person;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

휴식 종점 :

@ResponseBody
@RequestMapping(method = RequestMethod.GET)
public List<Person> listPersons() {
    return personService.findAll();
}

json 응답에는 ID를 제외한 모든 필드가 있습니다.이 필드는 편집 / 삭제하기 위해 프런트 엔드 측에서 필요합니다. ID를 직렬화하기 위해 스프링 부트를 어떻게 구성 할 수 있습니까?

이것이 바로 응답의 모습입니다.

[{
  "firstName": "Just",
  "middleName": "Test",
  "lastName": "Name",
  "comment": "Just a comment",
  "created": 1405774380410,
  "updated": null,
  "phoneNumbers": [{
    "phoneNumber": "74575754757"
  }, {
    "phoneNumber": "575757547"
  }, {
    "phoneNumber": "57547547547"
  }]
}]

UPD 양방향 Hibernate 매핑을 가지고 있습니다. 어쩌면 문제와 관련이있을 수 있습니다.

해결법

  1. ==============================

    1.나는 최근에 같은 문제가 있었는데, 그 이유는 스프링 부트 스타터 데이터 휴지가 기본적으로 어떻게 작동하는지 때문이다. 내 SO 질문보기 -> 응용 프로그램을 Spring Boot로 마이그레이션 한 후 Spring Data Rest를 사용하는 동안 @Id가있는 엔티티 속성이 더 이상 JSON에 마샬링되지 않는다는 것을 관찰했다.

    나는 최근에 같은 문제가 있었는데, 그 이유는 스프링 부트 스타터 데이터 휴지가 기본적으로 어떻게 작동하는지 때문이다. 내 SO 질문보기 -> 응용 프로그램을 Spring Boot로 마이그레이션 한 후 Spring Data Rest를 사용하는 동안 @Id가있는 엔티티 속성이 더 이상 JSON에 마샬링되지 않는다는 것을 관찰했다.

    동작 방식을 사용자 정의하려면 RepositoryRestConfigurerAdapter를 확장하여 특정 클래스의 ID를 표시 할 수 있습니다.

    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
    import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
    
    @Configuration
    public class RepositoryConfig extends RepositoryRestConfigurerAdapter {
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.exposeIdsFor(Person.class);
        }
    }
    
  2. ==============================

    2.@ eric-peladan의 답변은 아직까지는 작동하지 않았지만 꽤 가까웠습니다. 이전 버전의 Spring Boot에서 작동했을 수도 있습니다. 이제 이것이 어떻게 구성되어 대신 올바르다면 수정하십시오 :

    @ eric-peladan의 답변은 아직까지는 작동하지 않았지만 꽤 가까웠습니다. 이전 버전의 Spring Boot에서 작동했을 수도 있습니다. 이제 이것이 어떻게 구성되어 대신 올바르다면 수정하십시오 :

    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
    import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
    
    @Configuration
    public class RepositoryConfiguration extends RepositoryRestConfigurerAdapter {
    
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.exposeIdsFor(User.class);
            config.exposeIdsFor(Comment.class);
        }
    }
    
  3. ==============================

    3.

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
    import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
    
    import javax.persistence.EntityManager;
    import javax.persistence.metamodel.Type;
    
    @Configuration
    public class RestConfiguration extends RepositoryRestConfigurerAdapter {
    
        @Autowired
        private EntityManager entityManager;
    
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.exposeIdsFor(
                    entityManager.getMetamodel().getEntities().stream()
                    .map(Type::getJavaType)
                    .toArray(Class[]::new));
        }
    }
    
        ...
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.exposeIdsFor(
                    entityManager.getMetamodel().getEntities().stream()
                    .map(Type::getJavaType)
                    .filter(Identifiable.class::isAssignableFrom)
                    .toArray(Class[]::new));
        }
    
        ...
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.exposeIdsFor(
                    entityManager.getMetamodel().getEntities().stream()
                    .map(Type::getJavaType)
                    .filter(c -> c.isAnnotationPresent(ExposeId.class))
                    .toArray(Class[]::new));
        }
    

    샘플 주석 :

    import java.lang.annotation.*;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExposeId {}
    
  4. ==============================

    4.SpringBoot를 사용하면 SpringBootRepositoryRestMvcConfiguration을 확장해야합니다. RepositoryRestMvcConfiguration을 사용하면 application.properties의 구성 정의가 작동하지 않을 수 있습니다.

    SpringBoot를 사용하면 SpringBootRepositoryRestMvcConfiguration을 확장해야합니다. RepositoryRestMvcConfiguration을 사용하면 application.properties의 구성 정의가 작동하지 않을 수 있습니다.

    @Configuration
    public class MyConfiguration extends SpringBootRepositoryRestMvcConfiguration  {
    
    @Override
    protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(Project.class);
    }
    }
    

    그러나 일시적인 필요 투영을 사용하여 다음과 같은 직렬화에 ID를 포함 할 수 있습니다.

    @Projection(name = "allparam", types = { Person.class })
    public interface ProjectionPerson {
    Integer getIdPerson();
    String getFirstName();
    String getLastName();
    

    }

  5. ==============================

    5.

    @Component
    public class EntityExposingIdConfiguration extends RepositoryRestConfigurerAdapter {
    
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            try {
                Field exposeIdsFor = RepositoryRestConfiguration.class.getDeclaredField("exposeIdsFor");
                exposeIdsFor.setAccessible(true);
                ReflectionUtils.setField(exposeIdsFor, config, new ListAlwaysContains());
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        class ListAlwaysContains extends ArrayList {
    
            @Override
            public boolean contains(Object o) {
                return true;
            }
        }
    }
    
  6. ==============================

    6.흠, 해결책을 찾은 것처럼 보입니다. pom 파일에서 spring-boot-startter-data-rest를 제거하고 phoneNumbers에 @JsonManagedReference를 추가하고 person에게 @JsonBackReference를 추가하여 원하는 출력을 제공합니다. 응답의 Json는 이제는 더 이상 인쇄되지 않습니다. 그러나 이제는 이드가 있습니다. 이 마법에 대한 후드에서 어떤 마법의 스프링 부트가 수행되는지 모르지만 나는 그것을 좋아하지 않는다.

    흠, 해결책을 찾은 것처럼 보입니다. pom 파일에서 spring-boot-startter-data-rest를 제거하고 phoneNumbers에 @JsonManagedReference를 추가하고 person에게 @JsonBackReference를 추가하여 원하는 출력을 제공합니다. 응답의 Json는 이제는 더 이상 인쇄되지 않습니다. 그러나 이제는 이드가 있습니다. 이 마법에 대한 후드에서 어떤 마법의 스프링 부트가 수행되는지 모르지만 나는 그것을 좋아하지 않는다.

  7. ==============================

    7.쉬운 방법 : 변수의 이름을 개인용 긴 ID로 변경하십시오. 사적인 롱 이드에게.

    쉬운 방법 : 변수의 이름을 개인용 긴 ID로 변경하십시오. 사적인 롱 이드에게.

    나를 위해 일합니다. 여기에 대한 자세한 내용은 여기를 참조하십시오.

  8. from https://stackoverflow.com/questions/24839760/spring-boot-responsebody-doesnt-serialize-entity-id by cc-by-sa and MIT license