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之外,还有所有字段,我需要在前端编辑/删除人员。我如何配置spring引导来序列化Id呢?

这就是现在的反应:

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

UPD有双向休眠映射,也许这与问题有关。

我最近有同样的问题,这是因为这是spring-boot-starter-data-rest的默认工作方式。查看我的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);
    }
}

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

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

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

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

对于SpringBoot,您必须扩展SpringBootRepositoryRestMvcConfiguration
如果您使用RepositoryRestMvcConfiguration应用程序中定义的配置。属性可能无法工作

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

}

只需在Id中添加@JsonProperty注释,它就可以工作了。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JsonProperty
private long id;
另一种方法是在配置中实现RepositoryRestConfigurerAdapter。(当你必须在许多地方进行封送时,这种方法将非常有用)
@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;
        }
    }
}

嗯,看来我找到解决办法了。从pom文件中删除spring-boot-start -data-rest,并在phoneNumbers中添加@JsonManagedReference,在person中添加@JsonBackReference,可以得到所需的输出。响应中的Json不再是打印出来的,但现在它有Id了。不知道这个依赖项下的神奇弹簧引导是什么,但我不喜欢它:)

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

适合我。你可以在这里阅读更多信息

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

下面是代码片段

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

您还可以使用静态配置方法轻松启用在几行中暴露id。

From Spring Data Rest RepsositoryRestConfigurer docs:

static RepositoryRestConfigurer withConfig(Consumer<RepositoryRestConfiguration> consumer)

方便的方法,轻松地创建简单的RepositoryRestConfigurer实例,仅想调整RepositoryRestConfiguration。

参数:consumer -不能为空

自:

3.1

所以这适用于我在一个现有的@Configuration注释类:

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

最新更新