简体   繁体   English

Spring 引导@ResponseBody 不序列化实体 ID

[英]Spring boot @ResponseBody doesn't serialize entity id

Have a strange problem and can't figure out how to deal with it.有一个奇怪的问题,不知道如何处理。 Have simple POJO:有简单的 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);
    }
}

and rest endpoint:和 rest 端点:

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

In json response there are all fields except Id, which I need on front end side to edit/delete person.在 json 响应中有除 Id 之外的所有字段,我需要在前端编辑/删除人员。 How can I configure spring boot to serialize Id as well?我如何配置 spring 启动以序列化 Id?

That's how response looks like now:这就是现在的响应:

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

UPD Have bidirectional hibernate mapping, maybe it's somehow related to issue. UPD 有双向 hibernate 映射,也许它与问题有某种关系。

I recently had the same problem and it's because that's how spring-boot-starter-data-rest works by default.我最近遇到了同样的问题,这是因为默认情况下spring-boot-starter-data-rest工作的。 See my SO question -> 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请参阅我的 SO 问题 -> 在将应用程序迁移到 Spring Boot 后使用 Spring Data Rest 时,我观察到带有 @Id 的实体属性不再编组为 JSON

To customize how it behaves, you can extend RepositoryRestConfigurerAdapter to expose IDs for specific classes.要自定义其行为方式,您可以扩展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);
    }
}

In case you need to expose the identifiers for all entities :如果您需要公开所有实体的标识符:

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

Note that in versions of Spring Boot prior to 2.1.0.RELEASE you must extend the (now deprecated) org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter instead of implement RepositoryRestConfigurer directly.请注意,在2.1.0.RELEASE之前的 Spring Boot 版本中,您必须扩展(现已弃用) org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter而不是直接实现RepositoryRestConfigurer


If you only want to expose the identifiers of entities that extends or implements specific super class or interface :如果您只想公开扩展或实现特定超类或接口的实体的标识符:

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

If you only want to expose the identifiers of entities with a specific annotation :如果您只想公开具有特定注释的实体标识符:

    ...
    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(
                entityManager.getMetamodel().getEntities().stream()
                .map(Type::getJavaType)
                .filter(c -> c.isAnnotationPresent(ExposeId.class))
                .toArray(Class[]::new));
    }

Sample annotation:示例注释:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExposeId {}

Answer from @eric-peladan didn't work out of the box, but was pretty close, maybe that worked for previous versions of Spring Boot.来自@eric-peladan 的回答不是开箱即用的,但非常接近,也许这适用于以前版本的 Spring Boot。 Now this is how it is supposed to be configured instead, correct me if I'm wrong:现在这就是它应该如何配置,如果我错了,请纠正我:

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

The class RepositoryRestConfigurerAdapter has been deprecated since 3.1, implement RepositoryRestConfigurer directly.RepositoryRestConfigurerAdapter自 3.1 起已弃用,直接实现RepositoryRestConfigurer

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

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

With Spring Boot you have to extends SpringBootRepositoryRestMvcConfiguration使用 Spring Boot,您必须扩展SpringBootRepositoryRestMvcConfiguration
if you use RepositoryRestMvcConfiguration the configuration define in application.properties may not worked如果您使用RepositoryRestMvcConfiguration在 application.properties 中定义的配置可能不起作用

@Configuration
public class MyConfiguration extends SpringBootRepositoryRestMvcConfiguration  {

@Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
    config.exposeIdsFor(Project.class);
}
}

But for a temporary need You can use projection to include id in the serialization like :但是对于临时需要,您可以使用投影在序列化中包含id ,例如:

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

} }

Just add @JsonProperty annotation to the Id and it works.只需将@JsonProperty注释添加到 Id 即可。

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

another approach is to implement RepositoryRestConfigurerAdapter in configuration.另一种方法是在配置中实现RepositoryRestConfigurerAdapter (This approach will be usefull when you have to do marshalling in many places) (当您必须在许多地方进行编组时,此方法将很有用)

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

Hm, ok seems like I found the solution.嗯,好吧似乎我找到了解决方案。 Removing spring-boot-starter-data-rest from pom file and adding @JsonManagedReference to phoneNumbers and @JsonBackReference to person gives desired output.从 pom 文件中删除 spring-boot-starter-data-rest 并将 @JsonManagedReference 添加到 phoneNumbers 并将 @JsonBackReference 添加到 person 提供所需的输出。 Json in response isn't pretty printed any more but now it has Id.响应的 Json 不再打印出来,但现在它有 Id。 Don't know what magic spring boot performs under hood with this dependency but I don't like it :)不知道具有这种依赖性的神奇弹簧靴在引擎盖下执行了什么,但我不喜欢它:)

Easy way: rename your variable private Long id;简单的方法:重命名您的变量private Long id; to private Long Id;private Long Id;

Works for me.为我工作。 You can read more about it here你可以在这里阅读更多关于它的信息

Implement the RepositoryRestConfigurer and use @Configuration annotation on the class.实现RepositoryRestConfigurer并在 class 上使用@Configuration注释。

Here's the snippet这是片段

@Configuration
public class BasicConfig implements RepositoryRestConfigurer{

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

You can also use the static configuration method to easily enable exposing ids in a few lines.您还可以使用 static 配置方法在几行中轻松启用公开 ID。

From the Spring Data Rest RepsositoryRestConfigurer docs :来自Spring 数据 Rest RepsositoryRestConfigurer文档

static RepositoryRestConfigurer withConfig(Consumer<RepositoryRestConfiguration> consumer)

Convenience method to easily create simple RepositoryRestConfigurer instances that solely want to tweak the RepositoryRestConfiguration.轻松创建简单的 RepositoryRestConfigurer 实例的便捷方法,这些实例只想调整 RepositoryRestConfiguration。

Parameters: consumer - must not be null.参数: consumer - 不能是 null。

Since: 3.1从: 3.1

So this works for me in an existing @Configuration -annotated class:所以这在现有的@Configuration中对我有用 - 注释为 class:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;

@Configuration
public class ApplicationConfiguration {

    @Bean
    public RepositoryRestConfigurer repositoryRestConfigurer() {
        return RepositoryRestConfigurer.withConfig(repositoryRestConfiguration ->
            repositoryRestConfiguration.exposeIdsFor(Person.class)
        );
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM