【问题标题】:Spring boot @ResponseBody doesn't serialize entity idSpring boot @ResponseBody 不序列化实体 id
【发布时间】:2014-09-10 11:06:24
【问题描述】:

有一个奇怪的问题,不知道如何处理。 拥有简单的 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 之外的所有字段,我需要在前端编辑/删除人员。如何配置 spring boot 来序列化 Id?

现在的响应是这样的:

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

UPD 有双向休眠映射,可能与问题有关。

【问题讨论】:

  • 您能否向我们提供有关您的弹簧设置的更多见解?您使用什么 json 编组器?默认的,杰克逊,...?
  • 其实没有什么特别的设置。想尝试一下 spring boot :) 将 spring-boot-starter-data-rest 添加到 pom 并使用 @EnableAutoConfiguration 就是这样。阅读几个教程,似乎所有教程都必须开箱即用。它是,除了那个 Id 字段。更新了带有端点响应的帖子。
  • 在 Spring 4 中,您还应该在控制器类上使用 @RestController 并从方法中删除 @ResponseBody。另外我建议让 DTO 类来处理 json 请求/响应而不是域对象。

标签: java json spring spring-boot


【解决方案1】:

我最近遇到了同样的问题,这是因为spring-boot-starter-data-rest 默认情况下是这样工作的。请参阅我的 SO 问题 -> While using Spring Data Rest after migrating an app to Spring Boot, I have observed that entity properties with @Id are no longer marshalled to 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);
    }
}

【讨论】:

  • 并且不要忘记现在支持实体类中的 id 的 getter 和 setter!..(我忘记了,正在寻找很多时间)
  • 无法在org.springframework.data.rest.webmvc.config 中找到RepositoryRestConfigurerAdapter
  • 产量:The type RepositoryRestConfigurerAdapter is deprecated。似乎也不起作用
  • 确实RepositoryRestConfigurerAdapter在最新版本中已被弃用,您必须直接实现RepositoryRestConfigurer(使用implements RepositoryRestConfigurer而不是extends RepositoryRestConfigurerAdapter
【解决方案2】:

如果您需要公开所有实体的标识符:

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.RepositoryRestConfigurer;

import javax.persistence.EntityManager;
import javax.persistence.metamodel.Type;

@Configuration
public class RestConfiguration implements RepositoryRestConfigurer {

    @Autowired
    private EntityManager entityManager;

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(
                entityManager.getMetamodel().getEntities().stream()
                .map(Type::getJavaType)
                .toArray(Class[]::new));
    }
}

请注意,在 2.1.0.RELEASE 之前的 Spring Boot 版本中,您必须扩展 (现已弃用) org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter 而不是直接实现 RepositoryRestConfigurer


如果您只想公开扩展实体的标识符或 实现特定的超类或接口

    ...
    @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 {}

【讨论】:

  • 如果要使用第一个代码块,它会进入哪个目录和文件?
  • @DavidKrider :它应该在自己的文件中,在组件扫描涵盖的任何目录中。您的主应用程序下方的任何子包(带有@SpringBootApplication 注释)都应该没问题。
  • Field entityManager in com.myapp.api.config.context.security.RepositoryRestConfig required a bean of type 'javax.persistence.EntityManager' that could not be found.
  • 我尝试在 MyRepositoryRestConfigurerAdapter 中添加一个 @ConditionalOnBean(EntityManager.class) ,但是没有调用该方法,并且 id 仍然没有暴露。我将 Spring 数据与 spring data mybatis 一起使用:github.com/hatunet/spring-data-mybatis
  • @DimitriKopriwa :EntityManager 是 JPA 规范的一部分,MyBatis 没有实现 JPA(查看 Does MyBatis follows JPA?)。所以,我认为您应该使用config.exposeIdsFor(...) 方法,如accepted answer 中的方法,一一配置所有实体。
【解决方案3】:

@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);
    }
}

【讨论】:

  • 确认此解决方案在 Spring Boot v1.3.3.RELEASE 下运行良好,与@eric-peladan 提出的不同。
【解决方案4】:

RepositoryRestConfigurerAdapter 类自 3.1 起已弃用,直接实现 RepositoryRestConfigurer

@Configuration
public class RepositoryConfiguration implements RepositoryRestConfigurer  {
	@Override
	public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
		config.exposeIdsFor(YouClass.class);
		RepositoryRestConfigurer.super.configureRepositoryRestConfiguration(config);
	}
}

字体:https://docs.spring.io/spring-data/rest/docs/current-SNAPSHOT/api/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer.html

【讨论】:

    【解决方案5】:

    使用 Spring Boot,您必须扩展 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();
    

    }

    【讨论】:

    • 在 spring boot 2 中,这不起作用。 RepositoryRestMvcConfiguration 类没有 configureRepositoryRestConfiguration 来覆盖。
    【解决方案6】:

    只需将 @JsonProperty 注释添加到 Id 即可。

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonProperty
    private long id;
    

    另一种方法是在配置中实现RepositoryRestConfigurerAdapter。 (当您必须在许多地方进行编组时,这种方法会很有用)

    【讨论】:

      【解决方案7】:
      @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;
              }
          }
      }
      

      【讨论】:

      • 欢迎来到 SO!在您的答案中发布代码时,解释您的代码如何解决 OP 的问题会很有帮助:)
      【解决方案8】:

      嗯,好的,看来我找到了解决方案。从 pom 文件中删除 spring-boot-starter-data-rest 并将 @JsonManagedReference 添加到 phoneNumbers 并将 @JsonBackReference 添加到 person 可以提供所需的输出。作为响应的 Json 不再漂亮地打印出来,但现在它有了 Id。不知道这种依赖关系在引擎盖下执行了什么神奇的弹簧靴,但我不喜欢它:)

      【讨论】:

      • 它用于将 Spring Data 存储库公开为 REST 端点。如果您不想要该功能,可以将其省略。 id 与我相信由 SDR 注册的 Jackson Module 有关。
      • RESTful API 不应公开代理键 ID,因为它们对外部系统没有任何意义。在 RESTful 架构中,ID 是该资源的规范 URL。
      • 我以前读过这个,但老实说我不明白如何在没有 ID 的情况下链接前端和后端。我必须将 Id 传递给 orm 以进行删除/更新操作。也许你有一些链接,它如何以真正的 RESTful 方式执行:)
      【解决方案9】:

      简单方法:将变量 private Long id; 重命名为 private Long Id;

      为我工作。你可以阅读更多关于它here

      【讨论】:

      • 哦,伙计...这就是代码的味道...真的,不要那样做
      • @IgorDonin 对喜欢从变量/类/函数名称中嗅探和抓取语义的 Java 社区说...
      【解决方案10】:

      实现RepositoryRestConfigurer 并在类上使用@Configuration 注解。

      这是sn-p

      @Configuration
      public class BasicConfig implements RepositoryRestConfigurer{
      
          @Override
          public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
              // TODO Auto-generated method stub
              config.exposeIdsFor(Person.class);
          }
          
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-05
        • 2020-10-17
        • 2020-08-10
        • 2016-12-25
        • 2019-12-31
        • 2017-05-29
        • 2019-03-18
        相关资源
        最近更新 更多