Masz dziwny problem i nie potrafię sobie z nim poradzić. Miej proste 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);
}
}
i punkt końcowy odpoczynku:
@ResponseBody
@RequestMapping(method = RequestMethod.GET)
public List<Person> listPersons() {
return personService.findAll();
}
W odpowiedzi json są wszystkie pola z wyjątkiem Id, których potrzebuję po stronie interfejsu, aby edytować / usunąć osobę. Jak mogę skonfigurować rozruch sprężynowy, aby również serializować identyfikator?
Tak wygląda teraz odpowiedź:
[{
"firstName": "Just",
"middleName": "Test",
"lastName": "Name",
"comment": "Just a comment",
"created": 1405774380410,
"updated": null,
"phoneNumbers": [{
"phoneNumber": "74575754757"
}, {
"phoneNumber": "575757547"
}, {
"phoneNumber": "57547547547"
}]
}]
UPD Mają dwukierunkowe mapowanie hibernacji, być może jest to w jakiś sposób związane z problemem.
java
json
spring
spring-boot
Konstantin
źródło
źródło
@RestController
klasy kontrolera i usunąć@ResponseBody
z metod. Sugerowałbym również posiadanie klas DTO do obsługi żądań / odpowiedzi json zamiast obiektów domeny.Odpowiedzi:
Niedawno miałem ten sam problem i to dlatego, że tak
spring-boot-starter-data-rest
działa domyślnie. Zobacz moje pytanie SO -> Podczas korzystania z Spring Data Rest po migracji aplikacji do Spring Boot, zauważyłem, że właściwości jednostki z @Id nie są już kierowane do JSONAby dostosować sposób działania, możesz rozszerzyć,
RepositoryRestConfigurerAdapter
aby ujawnić identyfikatory dla określonych klas.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); } }
źródło
RepositoryRestConfigurerAdapter
worg.springframework.data.rest.webmvc.config
The type RepositoryRestConfigurerAdapter is deprecated
. Wygląda na to, że nie działaRepositoryRestConfigurerAdapter
w najnowszych wersjach jest przestarzałe, należy je wdrożyćRepositoryRestConfigurer
bezpośrednio (implements RepositoryRestConfigurer
zamiastextends RepositoryRestConfigurerAdapter
)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)); } }
Zauważ, że w wersjach Spring Boot wcześniejszych
2.1.0.RELEASE
musisz rozszerzyć (teraz przestarzałe)org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter
zamiastRepositoryRestConfigurer
bezpośrednio implementować .... @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)); }
Przykładowa adnotacja:
import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ExposeId {}
źródło
@SpringBootApplication
adnotacją) powinien działać.Field entityManager in com.myapp.api.config.context.security.RepositoryRestConfig required a bean of type 'javax.persistence.EntityManager' that could not be found.
@ConditionalOnBean(EntityManager.class)
inMyRepositoryRestConfigurerAdapter
, ale metoda nie jest wywoływana, a identyfikatory nadal nie są widoczne. Używam danych Spring z danymi wiosennymi mybatis: github.com/hatunet/spring-data-mybatisEntityManager
Jest częścią specyfikacji JPA, a MyBatis nie implementuje JPA (spójrz na Czy MyBatis podąża za JPA? ). Więc myślę, że powinieneś skonfigurować wszystkie encje jeden po drugim, używającconfig.exposeIdsFor(...)
metody jak w zaakceptowanej odpowiedzi .Odpowiedź od @ eric-peladan nie zadziałała po wyjęciu z pudełka, ale była dość bliska, może to zadziałało w poprzednich wersjach Spring Boot. Teraz tak powinno być skonfigurowane zamiast tego, popraw mnie, jeśli się mylę:
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); } }
źródło
W przypadku Spring Boot musisz rozszerzyć SpringBootRepositoryRestMvcConfiguration,
jeśli używasz RepositoryRestMvcConfiguration, konfiguracja zdefiniowana w application.properties może nie działać
@Configuration public class MyConfiguration extends SpringBootRepositoryRestMvcConfiguration { @Override protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { config.exposeIdsFor(Project.class); } }
Ale dla tymczasowej potrzeby możesz użyć projekcji, aby dołączyć identyfikator do serializacji, na przykład:
@Projection(name = "allparam", types = { Person.class }) public interface ProjectionPerson { Integer getIdPerson(); String getFirstName(); String getLastName();
}
źródło
Klasa
RepositoryRestConfigurerAdapter
została wycofana od wersji 3.1, zaimplementujRepositoryRestConfigurer
bezpośrednio.Czcionka: https://docs.spring.io/spring-data/rest/docs/current-SNAPSHOT/api/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer.html
źródło
Prosty sposób: zmień nazwę zmiennej
private Long id;
naprivate Long Id;
Pracuje dla mnie. Więcej na ten temat przeczytasz tutaj
źródło
@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; } } }
źródło
Hm, ok wygląda na to, że znalazłem rozwiązanie. Usunięcie spring-boot-starter-data-rest z pliku pom i dodanie @JsonManagedReference do phoneNumbers i @JsonBackReference do osoby daje pożądane wyniki. Json w odpowiedzi nie jest już ładnie wydrukowany, ale teraz ma Id. Nie wiem, jakie magiczne buty sprężynowe działają pod maską z taką zależnością, ale mi się to nie podoba :)
źródło
Module
że Jackson jest zarejestrowany przez SDR.